# Check if the script is running with administrative privileges if (-not (New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { Write-Host "This script requires administrative privileges..." Read-Host "Press Enter to end" exit } # Function to display instructions function Show-Instructions { Clear-Host Write-Host "==== OpenSSH Server Configuration Instructions ====" Write-Host "" Write-Host "This script configures the OpenSSH server on this Windows machine." Write-Host "After installing and configuring the server settings, you need to" Write-Host "generate and deploy SSH keys for secure access." Write-Host "" Write-Host "1. First, complete all necessary server configurations through this script." Write-Host " - Install or reinstall the OpenSSH Server." Write-Host " - Configure SSHD settings, the firewall rule, and SSH agent as needed." Write-Host "" Write-Host "2. After configuring the server, run the 'SSH_Key_Deployment_and_Connection.ps1'" Write-Host " script on your local machine to generate SSH keys and deploy them to this server." Write-Host "" Write-Host "3. Finally, use the key-based SSH connection to access this server securely." Write-Host "" Write-Host "Press Enter to return to the main menu..." Read-Host } # Function to install OpenSSH Server function Install-OpenSSHServer { <# .SYNOPSIS Installs the OpenSSH Server capability. .DESCRIPTION Installs the OpenSSH Server capability if it's available. .EXAMPLE Install-OpenSSHServer #> Install-OrReinstallOpenSSHServer -InstallOnly } # Function to reinstall OpenSSH Server function Reinstall-OpenSSHServer { <# .SYNOPSIS Reinstalls the OpenSSH Server capability. .DESCRIPTION Removes and reinstalls the OpenSSH Server capability if it's available. .EXAMPLE Reinstall-OpenSSHServer #> Install-OrReinstallOpenSSHServer -Reinstall } # Helper function to install or reinstall OpenSSH Server function Install-OrReinstallOpenSSHServer { param ( [switch]$InstallOnly, [switch]$Reinstall ) # Locate the OpenSSH Server capability $opensshCapability = Get-WindowsCapability -Online | Where-Object Name -like 'OpenSSH.Server*' if ($opensshCapability) { try { if ($Reinstall -and $opensshCapability.State -eq 'Installed') { # Remove OpenSSH Server capability Write-Output "Removing OpenSSH Server..." Remove-WindowsCapability -Online -Name $opensshCapability.Name -ErrorAction Stop Write-Output "OpenSSH Server removed successfully." } # Attempt to install OpenSSH Server capability Write-Output "Installing OpenSSH Server..." Add-WindowsCapability -Online -Name $opensshCapability.Name -ErrorAction Stop Write-Output "OpenSSH Server installed successfully." } catch { Write-Error "Failed to $(if ($Reinstall) {"reinstall"} else {"install"}) OpenSSH Server: $($_.Exception.Message)" } } else { Write-Error "OpenSSH Server capability not found." } } # Function to start and set OpenSSH service function Set-OpenSSHService { [string]$serviceName = 'sshd' if (Get-Service -Name $serviceName -ErrorAction SilentlyContinue) { try { Start-Service $serviceName Set-Service -Name $serviceName -StartupType 'Automatic' Write-Output "OpenSSH service started and set to start automatically." } catch { Write-Error "Failed to configure OpenSSH service: $($_.Exception.Message)" } } else { Write-Error "Service '$serviceName' does not exist." } } # Function to configure sshd_config file settings function Configure-SSHDConfig { param ( [string]$Port ) $configPath = "C:\ProgramData\ssh\sshd_config" $defaultConfigPath = "C:\Windows\System32\OpenSSH\sshd_config_default" # Check if the sshd_config file exists if (-not (Test-Path -Path $configPath)) { # If it doesn't exist, copy the default config file try { Copy-Item -Path $defaultConfigPath -Destination $configPath -Force Write-Output "Copied default sshd_config file to $configPath" } catch { Write-Error "Failed to copy default sshd_config file: $($_.Exception.Message)" return } } # Function to modify or add settings in sshd_config file function Set-ConfigLine { param ( [string]$FilePath, [string]$Setting, [string]$Value ) # Read the file content as a single string $content = Get-Content -Path $FilePath -Raw # Special handling for 'Port' to ensure it's placed before any 'Match' block if ($Setting -eq 'Port') { $pattern = "^\s*#?\s*Port\s+.*" # Match lines with or without comment # Check if the 'Port' line exists, either commented or uncommented if ($content -match $pattern) { # Replace the line with the uncommented port setting $content = $content -replace "^\s*#?\s*Port\s+.*", "Port $Value" } else { # If the setting doesn't exist, add it before any Match block $matchIndex = $content.IndexOf("Match ") if ($matchIndex -ne -1) { # Insert before the 'Match' block $content = $content.Insert($matchIndex, "`r`nPort $Value`r`n") } else { # If no 'Match' block, append to the end $content += "`r`nPort $Value" } } } else { # Generic pattern for other settings $pattern = "^\s*#?\s*$Setting\s+.*" if ($content -match $pattern) { # Replace the matched line with the uncommented version of the setting $content = $content -replace "^\s*#?\s*$Setting\s+.*", "$Setting $Value" } else { # If the setting isn't found, add it before any Match block or at the end $matchIndex = $content.IndexOf("Match ") if ($matchIndex -ne -1) { $content = $content.Insert($matchIndex, "$Setting $Value`r`n") } else { $content += "`r`n$Setting $Value" } } } # Write the updated content back to the file, trimming extra newlines Set-Content -Path $FilePath -Value ($content.TrimEnd() + "`r`n") } # Configure each setting, replacing any matching commented-out or uncommented lines Set-ConfigLine -FilePath $configPath -Setting 'Port' -Value $Port Set-ConfigLine -FilePath $configPath -Setting 'PasswordAuthentication' -Value 'no' Set-ConfigLine -FilePath $configPath -Setting 'PermitRootLogin' -Value 'no' Set-ConfigLine -FilePath $configPath -Setting 'MaxAuthTries' -Value '3' Set-ConfigLine -FilePath $configPath -Setting 'LoginGraceTime' -Value '20' Set-ConfigLine -FilePath $configPath -Setting 'PubkeyAuthentication' -Value 'yes' Set-ConfigLine -FilePath $configPath -Setting 'KbdInteractiveAuthentication' -Value 'no' # Restart SSH service after configuration function Restart-SSHService { try { Restart-Service sshd Write-Output "SSH service restarted successfully after configuration changes." } catch { Write-Error "Failed to restart SSH service: $($_.Exception.Message)" } } Restart-SSHService } # Function to create firewall rule with optional port and force creation function New-OpenSSHFirewallRule { param( [int]$Port, [switch]$Force ) $ruleName = "OpenSSH-Server-Custom-Port" $existingRule = Get-NetFirewallRule -Name $ruleName -ErrorAction SilentlyContinue if ($existingRule) { if ($Force) { # Remove existing rule if -Force is specified Remove-NetFirewallRule -Name $ruleName Write-Output "Existing firewall rule '$ruleName' was removed due to -Force." } else { # Notify if rule already exists and -Force is not specified Write-Output "Firewall rule '$ruleName' already exists. Use -Force to overwrite it." return } } # Attempt to create the new firewall rule try { New-NetFirewallRule -Name $ruleName -DisplayName 'OpenSSH Server (sshd)' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort $Port Write-Output "Firewall rule '$ruleName' created successfully on port $Port." } catch { Write-Error "Failed to create firewall rule: $($_.Exception.Message)" } } # Function to start and configure ssh-agent function Start-SSHAgent { $sshAgentService = Get-Service ssh-agent -ErrorAction SilentlyContinue if ($sshAgentService) { if ($sshAgentService.Status -eq "Running") { Write-Output "SSH Agent is already running." } else { try { Start-Service ssh-agent Set-Service -Name ssh-agent -StartupType 'Automatic' Write-Output "SSH Agent started and set to start automatically." } catch { Write-Error "Failed to start SSH Agent: $($_.Exception.Message)" } } } else { Write-Error "SSH Agent service does not exist." } } # Function to verify SSH setup, including service status check and local connection tests function Verify-SSHSetup { # Path to SSHD configuration $sshdConfigPath = "C:\ProgramData\ssh\sshd_config" # Display SSHD config port setting Write-Host "sshd_config port setting:" try { $portSetting = (Get-Content -Path $sshdConfigPath | Select-String -Pattern '^Port').Line Write-Output "Configured SSH Port: $portSetting" } catch { Write-Error "Could not retrieve Port setting from sshd_config: $($_.Exception.Message)" } # Display SSH firewall rule ports Write-Host "SSH server firewall rule port(s):" try { $firewallPorts = Get-NetFirewallRule -DisplayName 'OpenSSH Server (sshd)' | Get-NetFirewallPortFilter | ForEach-Object { $_.LocalPort } Write-Output "Firewall Rule Ports: $firewallPorts" } catch { Write-Error "Could not retrieve firewall rule ports: $($_.Exception.Message)" } # Check if sshd service is running Write-Host "Checking SSH service status..." $sshdService = Get-Service -Name 'sshd' -ErrorAction SilentlyContinue if ($sshdService -and $sshdService.Status -eq 'Running') { Write-Output "SSH service is running. Proceeding with connection tests..." # Connection test using password authentication Write-Host "Testing SSH connection with password authentication..." try { $passwordAuthResult = & ssh -o PreferredAuthentications=password -o ConnectTimeout=3 -p 1337 localhost "echo 'Password authentication successful'" 2>&1 if ($passwordAuthResult -match "Password authentication successful") { Write-Host "Password authentication was successful." } else { Write-Warning "Password authentication failed or is disabled." } } catch { Write-Error "An error occurred during password authentication test: $($_.Exception.Message)" } # Connection test using key authentication Write-Host "Testing SSH connection with key authentication..." try { $keyAuthResult = & ssh -o PreferredAuthentications=publickey -o ConnectTimeout=3 -p 1337 localhost "echo 'Key authentication successful'" 2>&1 if ($keyAuthResult -match "Key authentication successful") { Write-Host "Key authentication was successful." } else { Write-Warning "Key authentication failed or is disabled." } } catch { Write-Error "An error occurred during key authentication test: $($_.Exception.Message)" } } elseif ($sshdService) { Write-Host "SSH service is installed but currently stopped. Skipping connection tests." } else { Write-Error "SSH service ('sshd') is not installed or cannot be found." } Write-Host "`nSSH setup verification complete." } # Function to set up key-based authentication function Setup-KeyAuthentication { $sshDir = "$env:USERPROFILE\.ssh" $authKeysFile = "$sshDir\authorized_keys" # Ensure .ssh directory exists if (-not (Test-Path -Path $sshDir)) { New-Item -ItemType Directory -Path $sshDir -Force | Out-Null Write-Output ".ssh directory created at $sshDir" } # Create authorized_keys file with public key if it doesn't exist if (-not (Test-Path -Path $authKeysFile)) { # Generate keys if they don't exist if (-not (Test-Path -Path "$sshDir\id_ed25519.pub")) { ssh-keygen -t ed25519 -f "$sshDir\id_ed25519" -N "" | Out-Null Write-Output "SSH key pair generated." } # Copy public key to authorized_keys $publicKey = Get-Content -Path "$sshDir\id_ed25519.pub" -Raw Set-Content -Path $authKeysFile -Value $publicKey Write-Output "Public key added to authorized_keys." } else { Write-Output "authorized_keys file already exists." } # Set appropriate permissions on authorized_keys icacls $authKeysFile /inheritance:r /grant "$env:USERNAME:F" /grant "SYSTEM:F" | Out-Null Write-Output "Permissions set on authorized_keys." # Restart SSH service to apply changes Restart-Service sshd Write-Output "SSH service restarted to apply key-based authentication changes." } # Function to set up key-based authentication for administrator access function Setup-AdminKeyAuthentication { $sshDir = "C:\ProgramData\ssh" $authKeysFile = "$sshDir\administrators_authorized_keys" # Ensure ssh directory exists if (-not (Test-Path -Path $sshDir)) { New-Item -ItemType Directory -Path $sshDir -Force | Out-Null Write-Output "SSH directory created at $sshDir" } # Define paths for the admin key files $adminKeyPath = "$env:USERPROFILE\.ssh\admin_id_ed25519" $adminPublicKeyPath = "$adminKeyPath.pub" # Generate admin key pair if public key doesn't exist if (-not (Test-Path -Path $adminPublicKeyPath)) { Start-Process ssh-keygen -ArgumentList "-t", "ed25519", "-f", "$adminKeyPath" -NoNewWindow -Wait if (Test-Path -Path $adminPublicKeyPath) { Write-Output "Admin SSH key pair generated." } else { Write-Output "Error: Failed to generate admin SSH key pair." return # Exit if key generation failed } } else { Write-Output "Admin SSH key pair already exists." } # Copy public key to administrators_authorized_keys $publicKey = Get-Content -Path $adminPublicKeyPath -Raw Set-Content -Path $authKeysFile -Value $publicKey Write-Output "Admin public key added to administrators_authorized_keys." # Set appropriate permissions on administrators_authorized_keys icacls $authKeysFile /inheritance:r /grant "Administrators:F" /grant "SYSTEM:F" | Out-Null Write-Output "Permissions set on administrators_authorized_keys." # Restart SSH service to apply changes Restart-Service sshd Write-Output "SSH service restarted to apply administrator key-based authentication changes." } # Display menu function Show-Menu { Clear-Host Write-Host "1. Display Instructions" Write-Host "2. Install OpenSSH Server" Write-Host "3. Reinstall OpenSSH Server" Write-Host "4. Configure OpenSSH Service" Write-Host "5. Configure SSHD Settings" Write-Host "6. Add Firewall Rule" Write-Host "7. Start SSH Agent" Write-Host "8. Verify SSH Setup" Write-Host "9. Setup Key-Based Authentication for User" Write-Host "10. Setup Key-Based Authentication for Administrator" Write-Host "11. Exit" } # Update the main loop to include the new admin key authentication function do { Show-Menu $choice = Read-Host "Select an option [1-11]" switch ($choice) { "1" { Show-Instructions } "2" { Install-OpenSSHServer } "3" { Reinstall-OpenSSHServer } "4" { Set-OpenSSHService } "5" { $Port = Read-Host "Enter the SSH Port (default is 1337)" if (-not $Port) { $Port = "1337" } Configure-SSHDConfig -Port $Port } "6" { $Port = Read-Host "Enter the SSH Port for Firewall Rule (default is 1337)" if (-not $Port) { $Port = "1337" } New-OpenSSHFirewallRule -Port $Port } "7" { Start-SSHAgent } "8" { Verify-SSHSetup } "9" { Setup-KeyAuthentication } "10" { Setup-AdminKeyAuthentication } "11" { Write-Host "Exiting..."; break } default { Write-Host "Invalid selection. Please choose a valid option." } } Read-Host "Press Enter to continue..." } while ($choice -ne "11")