J2897 icon

OpenSSH Server Configurator

J2897 | PRO | 11/25/24 12:08:15 PM UTC | 0 ⭐ | 8078 👁️ | Never ⏰ | []
PowerShell |

17.17 KB

|

None

|

0 👍

/

0 👎

# 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")
 

Comments

  •  icon
    01/01/70 12:00:00 AM UTC
    Plain Text |

    0 B

    |

    👍

    /

    👎

    
        
  •  icon
    01/01/70 12:00:00 AM UTC
    Plain Text |

    0 B

    |

    👍

    /

    👎

    
        
  •  icon
    01/01/70 12:00:00 AM UTC
    Plain Text |

    0 B

    |

    👍

    /

    👎

    
        
  •  icon
    01/01/70 12:00:00 AM UTC
    Plain Text |

    0 B

    |

    👍

    /

    👎

    
        
  •  icon
    01/01/70 12:00:00 AM UTC
    Plain Text |

    0 B

    |

    👍

    /

    👎

    
        
  •  icon
    01/01/70 12:00:00 AM UTC
    Plain Text |

    0 B

    |

    👍

    /

    👎

    
        
  •  icon
    01/01/70 12:00:00 AM UTC
    Plain Text |

    0 B

    |

    👍

    /

    👎

    
        
  •  icon
    01/01/70 12:00:00 AM UTC
    Plain Text |

    0 B

    |

    👍

    /

    👎

    
        
  •  icon
    01/01/70 12:00:00 AM UTC
    Plain Text |

    0 B

    |

    👍

    /

    👎

    
        
  •  icon
    01/01/70 12:00:00 AM UTC
    Plain Text |

    0 B

    |

    👍

    /

    👎

    
        
  •  icon
    01/01/70 12:00:00 AM UTC
    Plain Text |

    0 B

    |

    👍

    /

    👎