342 lines
16 KiB
PowerShell
342 lines
16 KiB
PowerShell
# Function to validate email format
|
|
function Test-EmailFormat {
|
|
param([string]$Email)
|
|
$emailRegex = '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
|
|
return $Email -match $emailRegex
|
|
}
|
|
|
|
# Function to validate token format
|
|
function Test-TokenFormat {
|
|
param([string]$Token)
|
|
# Token should be 64 characters long and contain only hexadecimal characters
|
|
return $Token -match '^[a-fA-F0-9]{64}$'
|
|
}
|
|
|
|
# Function to validate Forgejo URL format
|
|
function Test-ForgejoUrl {
|
|
param([string]$Url)
|
|
# Basic URL validation - allows IP addresses and domain names
|
|
$urlRegex = '^https?://[a-zA-Z0-9.-]+(:\d+)?$|^[a-zA-Z0-9.-]+(:\d+)?$'
|
|
return $Url -match $urlRegex
|
|
}
|
|
|
|
# Function to generate signature
|
|
function Get-SshSignature {
|
|
param(
|
|
[string]$Token,
|
|
[string]$PrivateKeyPath
|
|
)
|
|
|
|
# Validate parameters
|
|
if (-not (Test-Path $PrivateKeyPath)) {
|
|
throw "Private key not found at path: $PrivateKeyPath"
|
|
}
|
|
|
|
# Normalize token format
|
|
$Token = $Token.Trim().ToLower()
|
|
|
|
# Debug: Check token format
|
|
Write-Host "`nDebug: Token format check:" -ForegroundColor Gray
|
|
Write-Host "Length: $($Token.Length)" -ForegroundColor Gray
|
|
Write-Host "Format: $($Token -match '^[a-f0-9]{64}$')" -ForegroundColor Gray
|
|
|
|
# Try Git Bash first if available
|
|
$gitBashPath = "C:\Program Files\Git\bin\bash.exe"
|
|
if (Test-Path $gitBashPath) {
|
|
Write-Host "`nUsing Git Bash for signature generation..." -ForegroundColor Gray
|
|
# Convert Windows path to Unix-style for Git Bash
|
|
$unixKeyPath = $PrivateKeyPath.Replace('\', '/')
|
|
# Use the exact command format from Forgejo, but with forgejo_token namespace
|
|
$bashCmd = "echo -n '$Token' | ssh-keygen -Y sign -n gitea -f '$unixKeyPath'"
|
|
Write-Host "Debug: Running Git Bash command:" -ForegroundColor Gray
|
|
Write-Host $bashCmd -ForegroundColor Gray
|
|
|
|
$rawSignatureOutput = & $gitBashPath -c $bashCmd 2>&1
|
|
} else {
|
|
Write-Host "`nGit Bash not found, using PowerShell..." -ForegroundColor Gray
|
|
# Use the exact PowerShell command format from Forgejo
|
|
$cmd = "cmd /c `<NUL set /p=`"$Token`"| ssh-keygen -Y sign -n gitea -f `"$PrivateKeyPath`""
|
|
Write-Host "Debug: Running PowerShell command:" -ForegroundColor Gray
|
|
Write-Host $cmd -ForegroundColor Gray
|
|
|
|
$rawSignatureOutput = Invoke-Expression $cmd 2>&1
|
|
}
|
|
|
|
# Debug: Check raw output
|
|
Write-Host "`nDebug: Raw signature output:" -ForegroundColor Gray
|
|
$rawSignatureOutput | ForEach-Object { Write-Host " $_" -ForegroundColor Gray }
|
|
|
|
# Filter the output to get only the signature block
|
|
$signatureLines = $rawSignatureOutput | Where-Object {
|
|
$_ -match '^(-----BEGIN SSH SIGNATURE-----|-----END SSH SIGNATURE-----|[A-Za-z0-9+/=]+)$'
|
|
}
|
|
|
|
# Join the lines with newlines
|
|
$signature = [string]::Join("`n", $signatureLines).Trim()
|
|
|
|
# Debug: Check filtered signature
|
|
Write-Host "`nDebug: Filtered signature:" -ForegroundColor Gray
|
|
$signature | ForEach-Object { Write-Host " $_" -ForegroundColor Gray }
|
|
|
|
# Verify signature format
|
|
if (-not ($signature -match '^-----BEGIN SSH SIGNATURE-----\r?\n[A-Za-z0-9+/=]+\r?\n-----END SSH SIGNATURE-----\r?$')) {
|
|
Write-Host "Warning: Signature format may be incorrect" -ForegroundColor Yellow
|
|
}
|
|
|
|
return $signature
|
|
}
|
|
|
|
# --- Get Forgejo Location ---
|
|
$forgejoLocation = ""
|
|
while (-not (Test-ForgejoUrl -Url $forgejoLocation)) {
|
|
$forgejoLocation = Read-Host "Please enter your Forgejo location (e.g., forgejo.example.com or 192.168.1.100:3000)"
|
|
if (-not (Test-ForgejoUrl -Url $forgejoLocation)) {
|
|
Write-Host "Invalid format. Please enter a valid URL or IP address." -ForegroundColor Red
|
|
}
|
|
}
|
|
|
|
# Remove http:// or https:// if present
|
|
$forgejoLocation = $forgejoLocation -replace '^https?://', ''
|
|
|
|
# --- SSH Key Generation ---
|
|
|
|
# Get user's email for key comment
|
|
$email = ""
|
|
while (-not (Test-EmailFormat -Email $email)) {
|
|
$email = Read-Host "Please enter your email address for your SSH key comment (e.g., user@example.com)"
|
|
if (-not (Test-EmailFormat -Email $email)) {
|
|
Write-Host "Invalid email format. Please try again." -ForegroundColor Red
|
|
}
|
|
}
|
|
|
|
# Generate SSH key
|
|
Write-Host "`n--- Generating SSH Key ---" -ForegroundColor Cyan
|
|
Write-Host "You'll be prompted for a passphrase. Please remember it and keep it safe!" -ForegroundColor Yellow
|
|
$privateKeyPath = "$env:USERPROFILE\.ssh\id_ed25519" # Define private key path
|
|
$publicKeyPath = "$env:USERPROFILE\.ssh\id_ed25519.pub" # Define public key path
|
|
|
|
# Check if key already exists to prevent accidental overwrite without warning
|
|
if (Test-Path $privateKeyPath) {
|
|
Write-Host "`nWarning: An SSH key already exists at '$privateKeyPath'." -ForegroundColor Yellow
|
|
$overwrite = Read-Host "Do you want to overwrite it? (Y/N)"
|
|
if ($overwrite -ne 'Y') {
|
|
Write-Host "Key generation cancelled. Exiting." -ForegroundColor Red
|
|
exit
|
|
}
|
|
}
|
|
|
|
# Execute ssh-keygen
|
|
ssh-keygen -t ed25519 -C $email -f $privateKeyPath # Explicitly define path
|
|
|
|
if ($LASTEXITCODE -ne 0) {
|
|
Write-Host "Error generating SSH key. Please check your SSH installation." -ForegroundColor Red
|
|
exit
|
|
}
|
|
|
|
# Add key to SSH agent (optional)
|
|
Write-Host "`n--- Adding Key to SSH Agent (Optional) ---" -ForegroundColor Yellow
|
|
Write-Host "This avoids re-entering your passphrase. You might be prompted for your passphrase now." -ForegroundColor Yellow
|
|
ssh-add $privateKeyPath
|
|
if ($LASTEXITCODE -ne 0) {
|
|
Write-Host "Failed to add key to SSH agent. You may need to start the 'OpenSSH Authentication Agent' service manually." -ForegroundColor Red
|
|
Write-Host "To start the service manually, run: Start-Service ssh-agent" -ForegroundColor Yellow
|
|
}
|
|
|
|
# Display the public key
|
|
Write-Host "`n--- Your Public Key ---" -ForegroundColor Green
|
|
Write-Host "Please copy the FOLLOWING ENTIRE KEY and paste it into Forgejo:" -ForegroundColor Green
|
|
Get-Content $publicKeyPath
|
|
Write-Host "---------------------------" -ForegroundColor Green
|
|
|
|
# --- Forgejo Web Server Instructions ---
|
|
|
|
Write-Host "`n--- Next Steps (Manual on Forgejo Web Server) ---" -ForegroundColor Cyan
|
|
Write-Host "1. Login to your Forgejo web server (e.g., https://$forgejoLocation)"
|
|
Write-Host "2. Click on your profile avatar (top right corner)"
|
|
Write-Host "3. Select 'Settings'"
|
|
Write-Host "4. In the left sidebar, click on 'SSH / GPG Keys'"
|
|
Write-Host "5. Click the 'Add Key' button"
|
|
Write-Host "6. Give your key a descriptive 'Title' (e.g., 'My Laptop Key')"
|
|
Write-Host "7. PASTE the public key shown ABOVE into the 'Content' text area"
|
|
Write-Host "8. Click 'Add Key'."
|
|
Write-Host "9. After adding, click the 'Verify' button next to your new key."
|
|
Write-Host "10. Copy the challenge token (the long hexadecimal string) provided by Forgejo."
|
|
|
|
# --- Key Verification ---
|
|
$verificationPassed = $false
|
|
$maxAttempts = 3
|
|
$attempts = 0
|
|
|
|
while (-not $verificationPassed -and $attempts -lt $maxAttempts) {
|
|
$attempts++
|
|
|
|
# Get and validate the token
|
|
$token = ""
|
|
while (-not (Test-TokenFormat -Token $token)) {
|
|
Write-Host "`n--- Key Verification (Attempt $attempts of $maxAttempts) ---" -ForegroundColor Yellow
|
|
Write-Host "Please get a fresh token from Forgejo (tokens expire after a few minutes)" -ForegroundColor Yellow
|
|
$token = Read-Host "Please paste the challenge token from Forgejo here:"
|
|
if (-not (Test-TokenFormat -Token $token)) {
|
|
Write-Host "Invalid token format. Token should be 64 hexadecimal characters." -ForegroundColor Red
|
|
}
|
|
}
|
|
|
|
# Verify the token using the PowerShell command format provided by Forgejo
|
|
Write-Host "`nVerifying key with Forgejo...`n" -ForegroundColor Yellow
|
|
Write-Host "Running verification command using private key: $privateKeyPath" -ForegroundColor Yellow
|
|
Write-Host "--------------------------------------------------------" -ForegroundColor Yellow
|
|
|
|
try {
|
|
# Generate signature using the function
|
|
$verificationOutput = Get-SshSignature -Token $token -PrivateKeyPath $privateKeyPath
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "SSH key signing failed with exit code $LASTEXITCODE"
|
|
}
|
|
|
|
Write-Host "`n--- Copy and Paste the Signature ---" -ForegroundColor Green
|
|
Write-Host "Please copy the FOLLOWING ENTIRE BLOCK (including BEGIN and END lines) and paste it back into the Forgejo web verification box:" -ForegroundColor Green
|
|
Write-Host "" # Add blank line before signature
|
|
$verificationOutput
|
|
Write-Host "" # Add blank line after signature
|
|
Write-Host "------------------------------------" -ForegroundColor Green
|
|
|
|
Write-Host "`n--- Final Steps (Manual on Forgejo Web Server) ---" -ForegroundColor Cyan
|
|
Write-Host "11. Paste the signature above into the verification box in Forgejo."
|
|
Write-Host "12. Click 'Verify'. You should see 'Successfully verified'."
|
|
Write-Host "13. Once verified, you can now test your SSH connection to Forgejo."
|
|
|
|
# Check verification status
|
|
Write-Host "`n--- Verification Status Check ---" -ForegroundColor Yellow
|
|
$verificationStatus = Read-Host "Did the verification pass in the Forgejo web interface? (Y/N)"
|
|
if ($verificationStatus -eq 'Y') {
|
|
$verificationPassed = $true
|
|
} else {
|
|
Write-Host "`nVerification failed. Here are some troubleshooting steps:" -ForegroundColor Red
|
|
Write-Host "1. Make sure you copied the ENTIRE signature block, including BEGIN and END lines" -ForegroundColor Yellow
|
|
Write-Host "2. Check that there are no extra spaces or line breaks in the signature" -ForegroundColor Yellow
|
|
Write-Host "3. Verify that the token was entered correctly" -ForegroundColor Yellow
|
|
Write-Host "4. The token may have expired - get a fresh token from Forgejo" -ForegroundColor Yellow
|
|
Write-Host "5. Try again with a new token" -ForegroundColor Yellow
|
|
|
|
if ($attempts -lt $maxAttempts) {
|
|
Write-Host "`nLet's try again with a new token..." -ForegroundColor Yellow
|
|
} else {
|
|
Write-Host "`nMaximum verification attempts reached. Please check your key and try again later." -ForegroundColor Red
|
|
exit
|
|
}
|
|
}
|
|
}
|
|
catch {
|
|
Write-Host "Error during verification: $_" -ForegroundColor Red
|
|
Write-Host "Output: $verificationOutput" -ForegroundColor Red
|
|
if ($attempts -lt $maxAttempts) {
|
|
Write-Host "`nLet's try again with a new token..." -ForegroundColor Yellow
|
|
} else {
|
|
Write-Host "`nMaximum verification attempts reached. Please check your key and try again later." -ForegroundColor Red
|
|
exit
|
|
}
|
|
}
|
|
}
|
|
|
|
# --- SSH Config Setup ---
|
|
Write-Host "`n--- SSH Config Setup (Optional) ---" -ForegroundColor Yellow
|
|
$setupConfig = Read-Host "Would you like to set up your SSH config for easier connections? (Y/N)"
|
|
if ($setupConfig -eq 'Y') {
|
|
$forgejoHost = $forgejoLocation
|
|
$forgejoUser = Read-Host "Enter your Forgejo username"
|
|
|
|
$sshConfigPath = "$env:USERPROFILE\.ssh\config"
|
|
# Convert Windows path to forward slashes for SSH config
|
|
$sshPrivateKeyPath = $privateKeyPath.Replace('\', '/')
|
|
$configContent = @"
|
|
|
|
# Forgejo Configuration
|
|
Host $forgejoHost
|
|
HostName $forgejoHost
|
|
User $forgejoUser
|
|
IdentityFile $sshPrivateKeyPath
|
|
PreferredAuthentications publickey
|
|
PubkeyAuthentication yes
|
|
PasswordAuthentication no
|
|
"@
|
|
|
|
# Create .ssh directory if it doesn't exist
|
|
if (-not (Test-Path "$env:USERPROFILE\.ssh")) {
|
|
New-Item -ItemType Directory -Path "$env:USERPROFILE\.ssh" | Out-Null
|
|
}
|
|
|
|
# Read existing config content as a single raw string
|
|
$existingRawConfig = ""
|
|
if (Test-Path $sshConfigPath) {
|
|
$existingRawConfig = Get-Content $sshConfigPath -Raw
|
|
}
|
|
|
|
# Define regex pattern to find and remove existing block for this host
|
|
# (?smi) flags: s=singleline (dot matches newline), m=multiline (^ and $ match start/end of line), i=case-insensitive
|
|
# ^Host\s+$([regex]::Escape($forgejoHost))\s*\r?\n - Matches the "Host" line
|
|
# (?:^\s*(?:HostName|User|IdentityFile|PreferredAuthentications|PubkeyAuthentication|PasswordAuthentication).*\r?\n)* - Matches subsequent indented config lines
|
|
$pattern = "(?smi)^Host\s+$([regex]::Escape($forgejoHost))\s*\r?\n(?:^\s*(?:HostName|User|IdentityFile|PreferredAuthentications|PubkeyAuthentication|PasswordAuthentication).*\r?\n)*"
|
|
|
|
# Remove existing block(s) for this host
|
|
$updatedRawConfig = $existingRawConfig -replace $pattern, ""
|
|
|
|
# Append the new configuration block
|
|
$updatedRawConfig += $configContent
|
|
|
|
# Write the updated content back to the file
|
|
Set-Content -Path $sshConfigPath -Value $updatedRawConfig.Trim() # Trim any leading/trailing whitespace from the whole file
|
|
|
|
Write-Host "`nSSH config has been updated. You can now connect using:" -ForegroundColor Green
|
|
Write-Host "ssh $forgejoHost" -ForegroundColor Cyan
|
|
Write-Host "`nYour SSH config is located at: $sshConfigPath" -ForegroundColor Yellow
|
|
|
|
# Wait a moment for the key to be fully processed
|
|
Write-Host "`nWaiting a moment for the key to be fully processed..." -ForegroundColor Yellow
|
|
Start-Sleep -Seconds 5
|
|
|
|
# Test the connection with verbose output
|
|
Write-Host "`n--- Testing SSH Connection ---" -ForegroundColor Yellow
|
|
Write-Host "Testing connection to $forgejoHost with verbose output..." -ForegroundColor Yellow
|
|
$testConnection = ssh -v -T $forgejoHost 2>&1
|
|
if ($LASTEXITCODE -eq 1) {
|
|
# Exit code 1 is actually success for SSH test connections
|
|
Write-Host "Connection successful! You can now use Git with your Forgejo repository." -ForegroundColor Green
|
|
} else {
|
|
Write-Host "Connection test failed. Here are some troubleshooting steps:" -ForegroundColor Red
|
|
Write-Host "1. Verify that your SSH key is properly added to your Forgejo account:" -ForegroundColor Yellow
|
|
Write-Host " - Check that the key appears in your SSH keys list" -ForegroundColor Yellow
|
|
Write-Host " - Verify that the key fingerprint matches: $((ssh-keygen -lf $publicKeyPath).Split(' ')[1])" -ForegroundColor Yellow
|
|
Write-Host "2. Check your SSH config:" -ForegroundColor Yellow
|
|
Write-Host " - Verify the hostname is correct: $forgejoHost" -ForegroundColor Yellow
|
|
Write-Host " - Verify the username is correct: $forgejoUser" -ForegroundColor Yellow
|
|
Write-Host " - Check that the key path is correct: $sshPrivateKeyPath" -ForegroundColor Yellow
|
|
Write-Host "3. Try connecting manually with verbose output:" -ForegroundColor Yellow
|
|
Write-Host " ssh -v -T $forgejoUser@$forgejoHost" -ForegroundColor Cyan
|
|
Write-Host "`nError output:" -ForegroundColor Red
|
|
$testConnection | ForEach-Object { Write-Host " $_" -ForegroundColor Red }
|
|
}
|
|
} else {
|
|
# If user didn't set up SSH config, still offer to test connection
|
|
Write-Host "`n--- Testing SSH Connection ---" -ForegroundColor Yellow
|
|
$testConnection = Read-Host "Would you like to test your SSH connection now? (Y/N)"
|
|
if ($testConnection -eq 'Y') {
|
|
$forgejoHost = $forgejoLocation
|
|
$forgejoUser = Read-Host "Enter your Forgejo username"
|
|
Write-Host "`nTesting connection to $forgejoUser@$forgejoHost with verbose output..." -ForegroundColor Yellow
|
|
$testResult = ssh -v -T "$forgejoUser@$forgejoHost" 2>&1
|
|
if ($LASTEXITCODE -eq 1) {
|
|
Write-Host "Connection successful! You can now use Git with your Forgejo repository." -ForegroundColor Green
|
|
} else {
|
|
Write-Host "Connection test failed. Here are some troubleshooting steps:" -ForegroundColor Red
|
|
Write-Host "1. Verify that your SSH key is properly added to your Forgejo account:" -ForegroundColor Yellow
|
|
Write-Host " - Check that the key appears in your SSH keys list" -ForegroundColor Yellow
|
|
Write-Host " - Verify that the key fingerprint matches: $((ssh-keygen -lf $publicKeyPath).Split(' ')[1])" -ForegroundColor Yellow
|
|
Write-Host "2. Try connecting manually with verbose output:" -ForegroundColor Yellow
|
|
Write-Host " ssh -v -T $forgejoUser@$forgejoHost" -ForegroundColor Cyan
|
|
Write-Host "`nError output:" -ForegroundColor Red
|
|
$testResult | ForEach-Object { Write-Host " $_" -ForegroundColor Red }
|
|
}
|
|
}
|
|
}
|
|
|
|
Write-Host "`nProcess for SSH key setup and verification completed!" -ForegroundColor Green
|