# 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 `&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