diff --git a/docs/reference/resources/Microsoft/OpenSSH/SSHD/sshd_config/index.md b/docs/reference/resources/Microsoft/OpenSSH/SSHD/sshd_config/index.md index 99752baa7..092c67141 100644 --- a/docs/reference/resources/Microsoft/OpenSSH/SSHD/sshd_config/index.md +++ b/docs/reference/resources/Microsoft/OpenSSH/SSHD/sshd_config/index.md @@ -56,11 +56,33 @@ configuration settings stored in the `sshd_config` file. The resource can: - The resource requires OpenSSH server to be installed on the system. - The resource must run in a process context that has permissions to read and write the `sshd_config` file. +- Configuration operations invoke `sshd -T`, which requires usable SSH server host keys. An elevated + process can still fail if those keys are missing or inaccessible. - On Windows, the default configuration file path is `%ProgramData%\ssh\sshd_config`. - On Linux, the default configuration file path is `/etc/ssh/sshd_config`. - To manage a configuration file in a non-default location, specify the [sshd_config_filepath](#sshd_config_filepath) property for the instance. +### Troubleshooting configuration validation + +When `sshd -T` fails, the resource returns the original OpenSSH diagnostic. To inspect the failure +directly, run `sshd -T` in the same process context. For a custom configuration, run +`sshd -T -f `. + +The message `sshd: no hostkeys available -- exiting` means that OpenSSH couldn't load a usable host +key. Check the preceding diagnostics to distinguish missing keys from keys that can't be read. +Elevation alone doesn't create missing host keys. + +On Windows, starting the installed `sshd` service initializes missing default host keys. If you need +to initialize the keys without starting the service, an administrator can run `ssh-keygen -A` to +generate missing default host keys. These are explicit setup actions; the resource doesn't generate +keys or start the service. If the configuration specifies custom `HostKey` paths, check those paths +instead. + +For `Permission denied` errors, check the permissions on the configuration file or host key named in +the diagnostic and the identity running DSC. Don't replace existing host keys or relax their +permissions as a general workaround. + ## Capabilities The resource has the following capabilities: diff --git a/resources/sshdconfig/locales/en-us.toml b/resources/sshdconfig/locales/en-us.toml index ae79fb88f..8be555870 100644 --- a/resources/sshdconfig/locales/en-us.toml +++ b/resources/sshdconfig/locales/en-us.toml @@ -104,6 +104,5 @@ seededConfigFromDefault = "Seeded missing sshd_config from '%{source}' to '%{tar sshdConfigDefaultNotFound = "'%{path}' does not exist and no default source could be found. Checked: %{paths}. Start the sshd service to initialize it, then retry." sshdConfigNotFoundNonWindows = "'%{path}' does not exist. Start the sshd service to initialize it, then retry." sshdConfigReadFailed = "failed to read sshd_config at path: '%{path}'" -sshdElevation = "elevated security context required" tempFileCreated = "temporary file created at: %{path}" tracingInitError = "Failed to initialize tracing" diff --git a/resources/sshdconfig/src/util.rs b/resources/sshdconfig/src/util.rs index dc83814a7..8dac8ce90 100644 --- a/resources/sshdconfig/src/util.rs +++ b/resources/sshdconfig/src/util.rs @@ -3,7 +3,7 @@ use rust_i18n::t; use serde_json::{Map, Value}; -use std::{path::PathBuf, process::Command}; +use std::{path::PathBuf, process::{Command, Output}}; use tracing::{debug, warn, Level}; use tracing_subscriber::{EnvFilter, Layer, prelude::__tracing_subscriber_SubscriberExt}; @@ -194,6 +194,10 @@ pub fn invoke_sshd_config_validation(args: Option) -> Result Result { if output.status.success() { let stdout = String::from_utf8(output.stdout) .map_err(|e| SshdConfigError::CommandError(e.to_string()))?; @@ -201,11 +205,6 @@ pub fn invoke_sshd_config_validation(args: Option) -> Result) -> Result assert_eq!(message, stderr), + result => panic!("Expected original sshd diagnostic, got {result:?}"), + } + } + } + + #[test] + fn returns_sshd_stdout_on_success() { + let stdout = "port 22\npasswordauthentication yes\n"; + let output = Output { + status: ExitStatus::from_raw(0), + stdout: stdout.as_bytes().to_vec(), + stderr: b"diagnostic on stderr\n".to_vec(), + }; + + assert_eq!(parse_sshd_output(output).unwrap(), stdout); + } +}