|
1 | 1 | function Install-SSHD |
2 | 2 | { |
3 | | - if (Get-OSVersion == "windows2019") { |
4 | | - # Microsoft privided OpenSSH must be installed on Windows 2019 |
5 | | - # => https://learn.microsoft.com/en-us/windows-server/administration/openssh/openssh_install_firstuse?tabs=powershell&pivots=windows-server-2019 |
6 | | - $sshPackages = @("OpenSSH.Client", "OpenSSH.Server") |
7 | | - foreach ($sshPackage in $sshPackages) { |
8 | | - try { |
9 | | - $moduleName = (Get-WindowsCapability -Online -Name "$sshPackage*" | ForEach-Object Name) |
10 | | - Add-WindowsCapability -Online -Name $moduleName |
11 | | - } catch { |
12 | | - Write-Output "Error installing $moduleName : $_" |
13 | | - } |
14 | | - } |
15 | | - } |
| 3 | + param ( |
| 4 | + [string]$SSHZipFile = $( Throw "Provide an SSHD zipfile" ) |
| 5 | + ) |
| 6 | + |
| 7 | + New-Item "$env:PROGRAMFILES\SSHTemp" -Type Directory -Force |
| 8 | + Open-Zip -ZipFile $SSHZipFile -OutPath "$env:PROGRAMFILES\SSHTemp" |
| 9 | + |
| 10 | + $ConfigPath = "$env:PROGRAMFILES\SSHTemp\OpenSSH-Win64\sshd_config_default" |
| 11 | + $ModifiedConfigContents = Edit-DefaultOpenSSHConfig -ConfigPath $ConfigPath |
| 12 | + Remove-Item -Force $ConfigPath |
| 13 | + Out-File -FilePath $ConfigPath -InputObject $ModifiedConfigContents -Encoding UTF8 |
| 14 | + |
| 15 | + Move-Item -Force "$env:PROGRAMFILES\SSHTemp\OpenSSH-Win64" "$env:PROGRAMFILES\OpenSSH" |
| 16 | + Remove-Item -Force "$env:PROGRAMFILES\SSHTemp" |
| 17 | + |
| 18 | + # Remove users from 'OpenSSH' before installing. The install process |
| 19 | + # will add back permissions for the NT AUTHORITY\Authenticated Users for some files |
| 20 | + Protect-Dir -path "$env:PROGRAMFILES\OpenSSH" |
16 | 21 |
|
17 | | - Edit-DefaultOpenSSHConfig |
| 22 | + Push-Location "$env:PROGRAMFILES\OpenSSH" |
| 23 | + powershell -ExecutionPolicy Bypass -File install-sshd.ps1 |
| 24 | + Pop-Location |
| 25 | + |
| 26 | + # # Grant NT AUTHORITY\Authenticated Users access to .EXEs and the .DLL in OpenSSH |
| 27 | + $FileNames = @( |
| 28 | + "libcrypto.dll", |
| 29 | + "scp.exe", |
| 30 | + "sftp-server.exe", |
| 31 | + "sftp.exe", |
| 32 | + "ssh-add.exe", |
| 33 | + "ssh-agent.exe", |
| 34 | + "ssh-keygen.exe", |
| 35 | + "ssh-keyscan.exe", |
| 36 | + "ssh-shellhost.exe", |
| 37 | + "ssh.exe", |
| 38 | + "sshd.exe" |
| 39 | + ) |
| 40 | + Invoke-CACL -FileNames $FileNames |
18 | 41 |
|
19 | 42 | Set-Service -Name sshd -StartupType Disabled |
| 43 | + # ssh-agent is not the same as ssh-agent in *nix openssh |
20 | 44 | Set-Service -Name ssh-agent -StartupType Disabled |
21 | 45 | } |
22 | 46 |
|
23 | 47 | function Enable-SSHD |
24 | 48 | { |
25 | | - # Remove existing OpenSSH firewall rule and recreate with '-Profile Any' option |
26 | | - if (Get-NetFirewallRule -Name "OpenSSH-Server-In-TCP" -ErrorAction SilentlyContinue) { |
27 | | - "Removing firewall rule: 'OpenSSH-Server-In-TCP'" |
28 | | - Remove-NetFirewallRule -Name "OpenSSH-Server-In-TCP" |
| 49 | + if ($null -eq (Get-NetFirewallRule | Where-Object { $_.DisplayName -ieq 'SSH' })) |
| 50 | + { |
| 51 | + "Creating firewall rule for SSH" |
| 52 | + New-NetFirewallRule -Protocol TCP -LocalPort 22 -Direction Inbound -Action Allow -DisplayName SSH |
| 53 | + } |
| 54 | + else |
| 55 | + { |
| 56 | + "Firewall rule for SSH already exists" |
| 57 | + } |
| 58 | + |
| 59 | + $InfFilePath = "$env:WINDIR\Temp\enable-ssh.inf" |
| 60 | + |
| 61 | + $InfFileContents = @' |
| 62 | +[Unicode] |
| 63 | +Unicode=yes |
| 64 | +[Version] |
| 65 | +signature=$CHICAGO$ |
| 66 | +Revision=1 |
| 67 | +[Registry Values] |
| 68 | +[System Access] |
| 69 | +[Privilege Rights] |
| 70 | +SeDenyNetworkLogonRight=*S-1-5-32-546 |
| 71 | +SeAssignPrimaryTokenPrivilege=*S-1-5-19,*S-1-5-20,*S-1-5-80-3847866527-469524349-687026318-516638107-1125189541 |
| 72 | +'@ |
| 73 | + $LGPOPath = "$env:WINDIR\LGPO.exe" |
| 74 | + if (Test-Path $LGPOPath) |
| 75 | + { |
| 76 | + Out-File -FilePath $InfFilePath -Encoding unicode -InputObject $InfFileContents -Force |
| 77 | + Try |
| 78 | + { |
| 79 | + Invoke-LGPO -LGPOPath $LGPOPath -InfFilePath $InfFilePath |
| 80 | + } |
| 81 | + Catch |
| 82 | + { |
| 83 | + throw "LGPO.exe failed with: $_.Exception.Message" |
| 84 | + } |
| 85 | + } |
| 86 | + else |
| 87 | + { |
| 88 | + "Did not find $LGPOPath. Assuming existing security policies are sufficient to support ssh." |
29 | 89 | } |
30 | | - Write-Output "Creating firewall rule 'OpenSSH-Server-In-TCP'" |
31 | | - New-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' -DisplayName 'OpenSSH Server (sshd)' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -Profile Any -LocalPort 22 |
32 | 90 |
|
33 | 91 | Set-Service -Name sshd -StartupType Automatic |
| 92 | + # ssh-agent is not the same as ssh-agent in *nix openssh |
34 | 93 | Set-Service -Name ssh-agent -StartupType Automatic |
35 | 94 |
|
36 | 95 | Remove-SSHKeys |
37 | 96 | } |
38 | 97 |
|
39 | 98 | function Remove-SSHKeys |
40 | 99 | { |
| 100 | + $SSHDir = "C:\Program Files\OpenSSH" |
| 101 | + |
| 102 | + Push-Location $SSHDir |
| 103 | + New-Item -ItemType Directory -Path "$env:ProgramData\ssh" -ErrorAction Ignore |
| 104 | + |
41 | 105 | "Removing any existing host keys" |
42 | 106 | Remove-Item -Path "$env:ProgramData\ssh\ssh_host_*" -ErrorAction Ignore |
| 107 | + Pop-Location |
43 | 108 | } |
44 | 109 |
|
45 | | -function Edit-DefaultOpenSSHConfig |
| 110 | +function Invoke-CACL |
46 | 111 | { |
47 | 112 | param ( |
48 | | - [string]$ConfigPath = "$env:windir\System32\OpenSSH\sshd_config_default", |
49 | | - [string]$GeneratedConfigPath = "$env:ProgramData\ssh\sshd_config" |
| 113 | + [string[]] $FileNames = $( Throw "Files not provided" ) |
50 | 114 | ) |
51 | 115 |
|
52 | | - Copy-Item -Path $ConfigPath -Destination "$ConfigPath.bak" |
53 | | - |
54 | | - $OriginalConfig = Get-Content $ConfigPath |
55 | | - Write-Output "Original SSH config at $ConfigPath :" |
56 | | - Write-Output $OriginalConfig |
| 116 | + foreach ($name in $FileNames) |
| 117 | + { |
| 118 | + $path = Join-Path "$env:PROGRAMFILES\OpenSSH" $name |
| 119 | + cacls.exe $Path /E /P "NT AUTHORITY\Authenticated Users:R" |
| 120 | + } |
| 121 | +} |
57 | 122 |
|
58 | | - $ModifiedConfig = $OriginalConfig ` |
59 | | - | ForEach-Object{ $_ -replace ".*Match Group administrators.*", "#$&" } ` |
60 | | - | ForEach-Object{ $_ -replace ".*AllowGroups administrators.*", "#$&" } ` |
61 | | - | ForEach-Object{ $_ -replace ".*AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys.*", "#$&" } ` |
62 | | - | ForEach-Object{ $_ -replace "#RekeyLimit default none", "$&`r`n# Disable cipher to mitigate CVE-2023-48795`r`nCiphers [email protected]`r`n" } |
| 123 | +function Invoke-LGPO |
| 124 | +{ |
| 125 | + param ( |
| 126 | + [string]$LGPOPath = $( Throw "Provide LGPO path" ), |
| 127 | + [string]$InfFilePath = $( Throw "Provide Inf file path" ) |
| 128 | + ) |
| 129 | + & $LGPOPath /s $InfFilePath |
| 130 | +} |
63 | 131 |
|
64 | | - Write-Output "Modified SSH config at $ConfigPath :" |
65 | | - Write-Output $ModifiedConfig |
| 132 | +function Edit-DefaultOpenSSHConfig |
| 133 | +{ |
| 134 | + param ( |
| 135 | + [string]$ConfigPath = $( Throw "Provide openssh default config path" ) |
| 136 | + ) |
66 | 137 |
|
67 | | - Remove-Item -Force $ConfigPath |
68 | | - Out-File -FilePath $ConfigPath -InputObject $ModifiedConfig -Encoding UTF8 |
| 138 | + $ModifiedConfig = Get-Content $ConfigPath ` |
| 139 | + | ForEach-Object{ $_ -replace ".*Match Group administrators.*", "#$&" } ` |
| 140 | + | ForEach-Object{ $_ -replace ".*AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys.*", "#$&" } ` |
| 141 | + | ForEach-Object{ $_ -replace "#RekeyLimit default none", "$&`r`n# Disable cipher to mitigate CVE-2023-48795`r`nCiphers [email protected]`r`n" } |
69 | 142 |
|
70 | | - # We need to make sure that the generated config is cleared, so our above changes are applied when the config |
71 | | - # is next generated. If this isnt done, then we may have a config from the prior template. |
72 | | - Remove-Item -Path $GeneratedConfigPath -ErrorAction Ignore |
| 143 | + return $ModifiedConfig |
73 | 144 | } |
0 commit comments