From dc57ef82fc3b32a9501e9dfaf727b704f96acf95 Mon Sep 17 00:00:00 2001 From: GraceSolutions Date: Mon, 4 May 2026 19:58:41 -0400 Subject: [PATCH] Change auth methods to opt-out, keep key generation opt-in Authentication methods now ENABLED by default (opt-out): - -DisablePubkeyAuthentication (was -EnablePubkeyAuthentication) - -DisablePasswordAuthentication (unchanged) - -DisableCertificateAuthentication (was -EnableCertificateAuthentication) Key generation remains OPT-IN: - -GenerateKeyPair still requires explicit use Config changes: - All auth settings now idempotent (only modify if value differs) - Certificate auth auto-enables if CA bundle exists - Disabling cert auth comments out TrustedUserCAKeys line Updated README with new parameter names and examples --- Invoke-OpenSSHConfiguration.ps1 | 122 +++++++++++++++++++++----------- README.md | 30 ++++---- 2 files changed, 94 insertions(+), 58 deletions(-) diff --git a/Invoke-OpenSSHConfiguration.ps1 b/Invoke-OpenSSHConfiguration.ps1 index fd09eaf..814545f 100644 --- a/Invoke-OpenSSHConfiguration.ps1 +++ b/Invoke-OpenSSHConfiguration.ps1 @@ -67,13 +67,13 @@ [Int]$KeyBits, [Parameter(Mandatory=$False)] - [Switch]$EnablePubkeyAuthentication, + [Switch]$DisablePubkeyAuthentication, [Parameter(Mandatory=$False)] [Switch]$DisablePasswordAuthentication, [Parameter(Mandatory=$False)] - [Switch]$EnableCertificateAuthentication, + [Switch]$DisableCertificateAuthentication, [Parameter(Mandatory=$False)] [Switch]$TrustWindowsCertificateAuthorities, @@ -679,59 +679,65 @@ Switch (Test-ProcessElevationStatus) $SSHDConfigContent = [System.IO.File]::ReadAllText($SSHDConfigPath.FullName) $SSHDConfigModified = $False - #region Enable PubkeyAuthentication - Switch ($EnablePubkeyAuthentication.IsPresent -eq $True) + #region Configure PubkeyAuthentication (enabled by default, opt-out) + $PubkeyAuthPattern = '(?m)^#?\s*PubkeyAuthentication\s+.*$' + $PubkeyAuthValue = Switch ($DisablePubkeyAuthentication.IsPresent) { {($_ -eq $True)} {'no'} Default {'yes'} } + $PubkeyAuthReplacement = "PubkeyAuthentication $($PubkeyAuthValue)" + + Switch ($SSHDConfigContent -imatch $PubkeyAuthPattern) { {($_ -eq $True)} { - $PubkeyAuthPattern = '(?m)^#?\s*PubkeyAuthentication\s+.*$' - $PubkeyAuthReplacement = 'PubkeyAuthentication yes' + $CurrentMatch = [System.Text.RegularExpressions.Regex]::Match($SSHDConfigContent, $PubkeyAuthPattern) - Switch ($SSHDConfigContent -imatch $PubkeyAuthPattern) + Switch ($CurrentMatch.Value -ine $PubkeyAuthReplacement) { {($_ -eq $True)} { $SSHDConfigContent = [System.Text.RegularExpressions.Regex]::Replace($SSHDConfigContent, $PubkeyAuthPattern, $PubkeyAuthReplacement) $SSHDConfigModified = $True - $WriteLogMessage.Invoke(0, @("Enabled PubkeyAuthentication.")) - } - - Default - { - $SSHDConfigContent = $SSHDConfigContent + "`r`n$($PubkeyAuthReplacement)" - $SSHDConfigModified = $True - $WriteLogMessage.Invoke(0, @("Added PubkeyAuthentication setting.")) + $WriteLogMessage.Invoke(0, @("Configured PubkeyAuthentication: $($PubkeyAuthValue)")) } } } + + Default + { + $SSHDConfigContent = $SSHDConfigContent + "`r`n$($PubkeyAuthReplacement)" + $SSHDConfigModified = $True + $WriteLogMessage.Invoke(0, @("Added PubkeyAuthentication: $($PubkeyAuthValue)")) + } } #endregion - #region Disable PasswordAuthentication - Switch ($DisablePasswordAuthentication.IsPresent -eq $True) + #region Configure PasswordAuthentication (enabled by default, opt-out) + $PasswordAuthPattern = '(?m)^#?\s*PasswordAuthentication\s+.*$' + $PasswordAuthValue = Switch ($DisablePasswordAuthentication.IsPresent) { {($_ -eq $True)} {'no'} Default {'yes'} } + $PasswordAuthReplacement = "PasswordAuthentication $($PasswordAuthValue)" + + Switch ($SSHDConfigContent -imatch $PasswordAuthPattern) { {($_ -eq $True)} { - $PasswordAuthPattern = '(?m)^#?\s*PasswordAuthentication\s+.*$' - $PasswordAuthReplacement = 'PasswordAuthentication no' + $CurrentMatch = [System.Text.RegularExpressions.Regex]::Match($SSHDConfigContent, $PasswordAuthPattern) - Switch ($SSHDConfigContent -imatch $PasswordAuthPattern) + Switch ($CurrentMatch.Value -ine $PasswordAuthReplacement) { {($_ -eq $True)} { $SSHDConfigContent = [System.Text.RegularExpressions.Regex]::Replace($SSHDConfigContent, $PasswordAuthPattern, $PasswordAuthReplacement) $SSHDConfigModified = $True - $WriteLogMessage.Invoke(0, @("Disabled PasswordAuthentication.")) - } - - Default - { - $SSHDConfigContent = $SSHDConfigContent + "`r`n$($PasswordAuthReplacement)" - $SSHDConfigModified = $True - $WriteLogMessage.Invoke(0, @("Added PasswordAuthentication setting (disabled).")) + $WriteLogMessage.Invoke(0, @("Configured PasswordAuthentication: $($PasswordAuthValue)")) } } } + + Default + { + $SSHDConfigContent = $SSHDConfigContent + "`r`n$($PasswordAuthReplacement)" + $SSHDConfigModified = $True + $WriteLogMessage.Invoke(0, @("Added PasswordAuthentication: $($PasswordAuthValue)")) + } } #endregion @@ -775,38 +781,70 @@ Switch (Test-ProcessElevationStatus) } #endregion - #region Configure TrustedUserCAKeys for Certificate Authentication - Switch (($EnableCertificateAuthentication.IsPresent -eq $True) -and ($TrustWindowsCertificateAuthorities.IsPresent -eq $True)) + #region Configure TrustedUserCAKeys for Certificate Authentication (enabled by default if CA bundle exists, opt-out) + $CABundleFileName = 'OpenSSH-TrustedCertificateAuthorities-LocalMachine.pem' + [System.IO.FileInfo]$CABundlePath = [System.IO.Path]::Combine($OpenSSHProgramDataDirectory.FullName, $CABundleFileName) + + #Use __PROGRAMDATA__ variable for sshd_config (portable path) + $CABundleConfigPath = "__PROGRAMDATA__/ssh/$($CABundleFileName)" + + $TrustedCAKeysPattern = '(?m)^#?\s*TrustedUserCAKeys\s+.*$' + + Switch ($DisableCertificateAuthentication.IsPresent -eq $True) { {($_ -eq $True)} { - $CABundleFileName = 'OpenSSH-TrustedCertificateAuthorities-LocalMachine.pem' - [System.IO.FileInfo]$CABundlePath = [System.IO.Path]::Combine($OpenSSHProgramDataDirectory.FullName, $CABundleFileName) - - #Use __PROGRAMDATA__ variable for sshd_config (portable path) - $CABundleConfigPath = "__PROGRAMDATA__/ssh/$($CABundleFileName)" - - Switch ($CABundlePath.Exists) + #Disable: Comment out TrustedUserCAKeys if it exists + Switch ($SSHDConfigContent -imatch $TrustedCAKeysPattern) + { + {($_ -eq $True)} + { + $CurrentMatch = [System.Text.RegularExpressions.Regex]::Match($SSHDConfigContent, $TrustedCAKeysPattern) + + Switch ($CurrentMatch.Value -inotmatch '^#') + { + {($_ -eq $True)} + { + $SSHDConfigContent = [System.Text.RegularExpressions.Regex]::Replace($SSHDConfigContent, $TrustedCAKeysPattern, "#$($CurrentMatch.Value)") + $SSHDConfigModified = $True + $WriteLogMessage.Invoke(0, @("Disabled TrustedUserCAKeys (commented out).")) + } + } + } + } + } + + Default + { + #Enable: Configure TrustedUserCAKeys if CA bundle exists + Switch ($CABundlePath.Exists -eq $True) { {($_ -eq $True)} { - $TrustedCAKeysPattern = '(?m)^#?\s*TrustedUserCAKeys\s+.*$' $TrustedCAKeysReplacement = "TrustedUserCAKeys $($CABundleConfigPath)" Switch ($SSHDConfigContent -imatch $TrustedCAKeysPattern) { {($_ -eq $True)} { - $SSHDConfigContent = [System.Text.RegularExpressions.Regex]::Replace($SSHDConfigContent, $TrustedCAKeysPattern, $TrustedCAKeysReplacement) - $SSHDConfigModified = $True - $WriteLogMessage.Invoke(0, @("Configured TrustedUserCAKeys to `"$($CABundleConfigPath)`".")) + $CurrentMatch = [System.Text.RegularExpressions.Regex]::Match($SSHDConfigContent, $TrustedCAKeysPattern) + + Switch ($CurrentMatch.Value -ine $TrustedCAKeysReplacement) + { + {($_ -eq $True)} + { + $SSHDConfigContent = [System.Text.RegularExpressions.Regex]::Replace($SSHDConfigContent, $TrustedCAKeysPattern, $TrustedCAKeysReplacement) + $SSHDConfigModified = $True + $WriteLogMessage.Invoke(0, @("Configured TrustedUserCAKeys: $($CABundleConfigPath)")) + } + } } Default { $SSHDConfigContent = $SSHDConfigContent + "`r`n$($TrustedCAKeysReplacement)" $SSHDConfigModified = $True - $WriteLogMessage.Invoke(0, @("Added TrustedUserCAKeys setting.")) + $WriteLogMessage.Invoke(0, @("Added TrustedUserCAKeys: $($CABundleConfigPath)")) } } } diff --git a/README.md b/README.md index 1f47f6c..a9f57b0 100644 --- a/README.md +++ b/README.md @@ -34,12 +34,12 @@ cd Invoke-OpenSSHConfiguration |-----------|------|-------------| | `-Mode` | String | Installation mode: `Client` or `Server` | | `-PublicKeyList` | String[] | Array of public keys to add to `administrators_authorized_keys` | -| `-GenerateKeyPair` | Switch | Generate SSH host key pair | +| `-GenerateKeyPair` | Switch | Generate SSH host key pair (opt-in) | | `-KeyType` | String | Key algorithm: `rsa`, `ecdsa`, `ed25519` (default: `ed25519`) | | `-KeyBits` | Int | Key size: 256, 384, 521 (ECDSA) or 2048, 3072, 4096 (RSA) | -| `-EnablePubkeyAuthentication` | Switch | Enable `PubkeyAuthentication yes` in sshd_config | -| `-DisablePasswordAuthentication` | Switch | Disable password authentication (`PasswordAuthentication no`) | -| `-EnableCertificateAuthentication` | Switch | Configure `TrustedUserCAKeys` for certificate auth | +| `-DisablePubkeyAuthentication` | Switch | Disable public key authentication (enabled by default) | +| `-DisablePasswordAuthentication` | Switch | Disable password authentication (enabled by default) | +| `-DisableCertificateAuthentication` | Switch | Disable certificate authentication (enabled by default if CA bundle exists) | | `-TrustWindowsCertificateAuthorities` | Switch | Export Windows CA certificates to PEM bundle | | `-CAInclusionExpression` | String | Regex to filter CAs by Subject or Thumbprint (include matches) | | `-CAExclusionExpression` | String | Regex to filter CAs by Subject or Thumbprint (exclude matches) | @@ -96,22 +96,20 @@ This exports all unexpired Root and Intermediate CA certificates from the Window ### Full Server Configuration ```powershell +# All auth methods enabled by default, just add keys and trust CAs .\Invoke-OpenSSHConfiguration.ps1 -Mode Server ` -PublicKeyList @('ssh-ed25519 AAAAC3NzaC1lZDI1NTE5... admin@corp') ` -GenerateKeyPair ` -KeyType ed25519 ` -TrustWindowsCertificateAuthorities ` - -EnableCertificateAuthentication ` - -EnablePubkeyAuthentication ` - -DisablePasswordAuthentication ` -LogDirectory 'C:\Logs\OpenSSH' ``` ### Hardened Server (Keys Only, No Passwords) ```powershell +# Disable password auth, keep pubkey and cert auth enabled .\Invoke-OpenSSHConfiguration.ps1 -Mode Server ` - -EnablePubkeyAuthentication ` -DisablePasswordAuthentication ``` @@ -121,20 +119,17 @@ This exports all unexpired Root and Intermediate CA certificates from the Window # Only trust CAs with "Corp" or "Enterprise" in the subject .\Invoke-OpenSSHConfiguration.ps1 -Mode Server ` -TrustWindowsCertificateAuthorities ` - -CAInclusionExpression '(Corp|Enterprise)' ` - -EnableCertificateAuthentication + -CAInclusionExpression '(Corp|Enterprise)' # Trust all CAs except Microsoft root CAs .\Invoke-OpenSSHConfiguration.ps1 -Mode Server ` -TrustWindowsCertificateAuthorities ` - -CAExclusionExpression 'Microsoft' ` - -EnableCertificateAuthentication + -CAExclusionExpression 'Microsoft' # Trust specific CA by thumbprint pattern .\Invoke-OpenSSHConfiguration.ps1 -Mode Server ` -TrustWindowsCertificateAuthorities ` - -CAInclusionExpression '^A1B2C3' ` - -EnableCertificateAuthentication + -CAInclusionExpression '^A1B2C3' ``` ### Custom SFTP Server Path @@ -210,13 +205,16 @@ The toolkit includes reusable functions in `Toolkit\Functions\`: - No certificate expiry warnings ### Features Implemented -- ✅ Password authentication control (`-DisablePasswordAuthentication`) +- ✅ Authentication methods enabled by default (opt-out via `-Disable*` switches) + - PubKey, Password, Certificate auth all enabled unless explicitly disabled +- ✅ Key generation opt-in (`-GenerateKeyPair`) - ✅ Service auto-start and start (automatic, no switches needed) - ✅ Windows Firewall rules (automatic per-profile: Domain, Private, Public) - ✅ Port configuration (`-SSHPort`, default 22) -- ✅ Config backup before modification (automatic timestamped backups) +- ✅ Config backup before modification (automatic, keeps last 3) - ✅ Config validation (`sshd -t`) before restart with automatic rollback - ✅ CA selection via regex (`-CAInclusionExpression`, `-CAExclusionExpression`) +- ✅ Session user certificate access (reads from HKEY_USERS when running as SYSTEM) - ✅ SFTP subsystem (configured automatically, custom path via `-SFTPSubsystemPath`) ## Troubleshooting