Skip to content

Add sshdconfig get #1004

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 21 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 16 commits
Commits
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