Skip to content
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
d268733
add sshdcmdargs struct and method to retrieve defaults
tgauth Jul 11, 2025
ebe3308
add default option to get
tgauth Jul 11, 2025
f169871
cleanup get
tgauth Jul 11, 2025
b8356b2
fix merge conflicts
tgauth Jul 18, 2025
0d65d5d
add e2e sshdconfig tests for get/export and update schema
tgauth Jul 18, 2025
2e82fc7
cleanup get/export display
tgauth Jul 21, 2025
812e1cc
update get to read _metadata
tgauth Jul 24, 2025
8bf176d
support custom sshdconfig filepath for get tests
tgauth Jul 24, 2025
b881d7d
update toml
tgauth Jul 24, 2025
894c9b0
add comment to struct
tgauth Jul 24, 2025
011bd8c
fix clippy
tgauth Jul 24, 2025
db2d25a
fix skip logic
tgauth Jul 24, 2025
af54285
fix i8n
tgauth Jul 25, 2025
4695d56
Revert "fix i8n"
tgauth Jul 25, 2025
3acbbdd
fix i8n take 2
tgauth Jul 25, 2025
c72f6a5
fix i8n take 3
tgauth Jul 25, 2025
484003c
use copilot suggestions
tgauth Jul 25, 2025
f5b619f
address Steve's feedback
tgauth Jul 28, 2025
5e2ab58
Update en-us.toml
tgauth Jul 28, 2025
85f9a7b
Merge branch 'main' into add-sshdconfig-get
tgauth Aug 5, 2025
64e1dfc
add check for sshd in test discovery
tgauth Aug 5, 2025
8ad00ad
add newline
tgauth Aug 6, 2025
dba561a
combine export and get command behavior
tgauth Aug 7, 2025
ea05024
update get and export _includeDefaults behavior
tgauth Aug 14, 2025
21da9c0
add _inheritedDefaults functionality
tgauth Aug 14, 2025
11702a0
update parser for match
tgauth Aug 14, 2025
33ab856
modify export behavior
tgauth Aug 14, 2025
2c348ef
add localization
tgauth Aug 14, 2025
d0c61da
fix localization
tgauth Aug 14, 2025
97c767e
Merge branch 'main' into add-sshdconfig-get
tgauth Aug 15, 2025
5474461
Merge branch 'main' into add-sshdconfig-get
tgauth Aug 19, 2025
688e8d3
update tests
tgauth Aug 20, 2025
575f0f8
Merge branch 'main' into add-sshdconfig-get
tgauth Sep 4, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 105 additions & 0 deletions dsc/tests/dsc_sshdconfig.tests.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

Describe 'SSHDConfig resource tests' {
BeforeAll {
$brewExists = ($null -ne (Get-Command brew -CommandType Application -ErrorAction Ignore))
$sshdExists = ($null -ne (Get-Command sshd -CommandType Application -ErrorAction Ignore))
$isAdmin = if ($IsWindows) {
$identity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
[System.Security.Principal.WindowsPrincipal]::new($identity).IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)
}
else {
[System.Environment]::UserName -eq 'root'
}
$runTest = $sshdExists -and $isAdmin
$yaml = @'
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
metadata:
Microsoft.DSC:
securityContext: elevated
resources:
- name: sshdconfig
type: Microsoft.OpenSSH.SSHD/sshd_config
properties:
'@
# set a non-default value in a temporary sshd_config file
"LogLevel Debug3" | Set-Content -Path $TestDrive/test_sshd_config
}

AfterAll {
if (Test-Path $TestDrive/test_sshd_config) {
Remove-Item -Path $TestDrive/test_sshd_config -Force
}
}

It 'Export works' {
if ($runTest) {
$out = dsc config export -i "$yaml" | ConvertFrom-Json -Depth 10
$LASTEXITCODE | Should -Be 0
$out.resources.count | Should -Be 1
$out.resources[0].properties | Should -Not -BeNullOrEmpty
$out.resources[0].properties.port[0] | Should -Be 22
}
}

It 'Get works'{
if ($runTest) {
$out = dsc config get -i "$yaml" | ConvertFrom-Json -Depth 10
$LASTEXITCODE | Should -Be 0
$out.results.count | Should -Be 1
$out.results.metadata.defaults | Should -Be $true
$out.results.result.actualState | Should -Not -BeNullOrEmpty
$out.results.result.actualState.port | Should -Be 22
$out.results.result.actualState.passwordAuthentication | Should -Be 'yes'
}
}

It 'Get with a specific setting works' {
if ($runTest) {
$get_yaml = @'
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
metadata:
Microsoft.DSC:
securityContext: elevated
resources:
- name: sshdconfig
type: Microsoft.OpenSSH.SSHD/sshd_config
properties:
passwordauthentication: 'no'
'@
$out = dsc config get -i "$get_yaml" | ConvertFrom-Json -Depth 10
$LASTEXITCODE | Should -Be 0
$out.results.count | Should -Be 1
$out.results.result.actualState.count | Should -Be 1
$out.results.result.actualState.passwordauthentication | Should -Be 'yes'
$out.results.result.actualState.port | Should -BeNullOrEmpty
}
}

It 'Get with defaults excluded works' {
if ($runTest) {
$filepath = Join-Path $TestDrive 'test_sshd_config'
$get_yaml = @"
`$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
metadata:
Microsoft.DSC:
securityContext: elevated
resources:
- name: sshdconfig
type: Microsoft.OpenSSH.SSHD/sshd_config
properties:
_metadata:
defaults: false
filepath: $filepath
"@
$out = dsc config get -i "$get_yaml" | ConvertFrom-Json -Depth 10
$LASTEXITCODE | Should -Be 0
$out.results.count | Should -Be 1
$out.results.metadata.defaults | Should -Be $false
$out.results.result.actualState.count | Should -Be 1
$out.results.result.actualState.port | Should -Not -Be 22
$out.results.result.actualState.loglevel | Should -Be 'debug3'
}
}
}
60 changes: 58 additions & 2 deletions sshdconfig/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions sshdconfig/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ rust-i18n = { version = "3.1" }
schemars = "0.8"
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0", features = ["preserve_order"] }
tempfile = "3.8"
thiserror = { version = "2.0" }
tracing = "0.1.37"
tracing-subscriber = { version = "0.3.17", features = ["ansi", "env-filter", "json"] }
Expand Down
8 changes: 6 additions & 2 deletions sshdconfig/locales/en-us.toml
Original file line number Diff line number Diff line change
@@ -1,25 +1,28 @@
_version = 1

[args]
getInput = "input to get from sshd_config"
setInput = "input to set in sshd_config"

[error]
command = "Command"
invalidInput = "Invalid Input"
io = "IO"
json = "JSON"
language = "Language"
notImplemented = "Not Implemented"
parser = "Parser"
parseInt = "Parse Integer"
persist = "Persist"
registry = "Registry"

[get]
debugSetting = "Get setting:"
defaultsMustBeBoolean = "defaults value must be true or false"
defaultShellCmdOptionMustBeString = "cmdOption must be a string"
defaultShellEscapeArgsMustBe0Or1 = "'%{input}' must be a 0 or 1"
defaultShellEscapeArgsMustBeDWord = "escapeArguments must be a DWord"
defaultShellMustBeString = "shell must be a string"
notImplemented = "get not yet implemented for Microsoft.OpenSSH.SSHD/sshd_config"
filepathMustBeString = "filePath must be a string"
windowsOnly = "Microsoft.OpenSSH.SSHD/Windows is only applicable to Windows"

[main]
Expand Down Expand Up @@ -51,5 +54,6 @@ shellPathDoesNotExist = "shell path does not exist: '%{shell}'"
shellPathMustNotBeRelative = "shell path must not be relative"

[util]
metadataMustBeObject = "_metadata must be an object"
sshdElevation = "elevated security context required"
tracingInitError = "Failed to initialize tracing"
2 changes: 2 additions & 0 deletions sshdconfig/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ pub struct Args {
pub enum Command {
/// Get default shell, eventually to be used for `sshd_config` and repeatable keywords
Get {
#[clap(short = 'i', long, help = t!("args.getInput").to_string())]
input: Option<String>,
#[clap(short = 's', long, hide = true)]
setting: Setting,
},
Expand Down
7 changes: 5 additions & 2 deletions sshdconfig/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT License.

use rust_i18n::t;
use tempfile::PersistError;
use thiserror::Error;

#[derive(Debug, Error)]
Expand All @@ -10,16 +11,18 @@ pub enum SshdConfigError {
CommandError(String),
#[error("{t}: {0}", t = t!("error.invalidInput"))]
InvalidInput(String),
#[error("{t}: {0}", t = t!("error.io"))]
IOError(#[from] std::io::Error),
#[error("{t}: {0}", t = t!("error.json"))]
Json(#[from] serde_json::Error),
#[error("{t}: {0}", t = t!("error.language"))]
LanguageError(#[from] tree_sitter::LanguageError),
#[error("{t}: {0}", t = t!("error.notImplemented"))]
NotImplemented(String),
#[error("{t}: {0}", t = t!("error.parser"))]
ParserError(String),
#[error("{t}: {0}", t = t!("error.parseInt"))]
ParseIntError(#[from] std::num::ParseIntError),
#[error("{t}: {0}", t = t!("error.persist"))]
PersistError(#[from] PersistError),
#[cfg(windows)]
#[error("{t}: {0}", t = t!("error.registry"))]
RegistryError(#[from] registry_lib::error::RegistryError),
Expand Down
31 changes: 25 additions & 6 deletions sshdconfig/src/export.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,38 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

use serde_json::{Map, Value};

use crate::error::SshdConfigError;
use crate::parser::parse_text_to_map;
use crate::util::invoke_sshd_config_validation;
use crate::util::{invoke_sshd_config_validation, SshdCommandArgs};

/// Invoke the export command.
/// Invoke the export command and return a map.
///
/// # Errors
///
/// This function will return an error if the command cannot invoke sshd -T, parse the return, or convert it to json.
pub fn invoke_export() -> Result<(), SshdConfigError> {
let sshd_config_text = invoke_sshd_config_validation()?;
let sshd_config: serde_json::Map<String, serde_json::Value> = parse_text_to_map(&sshd_config_text)?;
let json = serde_json::to_string(&sshd_config)?;
///
/// # Returns
///
/// This function will return `Ok(Map<String, Value>)` if the export is successful.
pub fn invoke_export_to_map(sshd_args: Option<SshdCommandArgs>) -> Result<Map<String, Value>, SshdConfigError> {
let sshd_config_text = invoke_sshd_config_validation(sshd_args)?;
let sshd_config: Map<String, Value> = parse_text_to_map(&sshd_config_text)?;
Ok(sshd_config)
}

/// Invoke the export command and print the result as JSON.
///
/// # Errors
/// This function will return an error if the export fails to retrieve the sshd configuration or convert it to JSON.
///
/// # Returns
///
/// This function will return `Ok(())` if the export is successful.
pub fn invoke_export(sshd_args: Option<SshdCommandArgs>) -> Result<(), SshdConfigError> {
let result = invoke_export_to_map(sshd_args)?;
let json = serde_json::to_string(&result)?;
println!("{json}");
Ok(())
}
Loading
Loading