Skip to content

Instantly share code, notes, and snippets.

@mavaddat
Forked from glsorre/keychain.ps1
Last active January 22, 2022 03:10
Show Gist options
  • Save mavaddat/a15fba958ee0f7d07043c8ade09959ec to your computer and use it in GitHub Desktop.
Save mavaddat/a15fba958ee0f7d07043c8ade09959ec to your computer and use it in GitHub Desktop.
Windows git and Windows Subsystem for Linux will never prompt ssh passphrase again
# To avoid having to re-type the password for SSH
test -f /usr/bin/keychain && eval $(/usr/bin/keychain --eval --quiet id_rsa)
# To avoid having to re-type the password for SSH
keychain --agents ssh --quiet id_rsa
#!/bin/fish
set SSH_ASKPASS_SCRIPT /tmp/ssh-askpass-script
touch $SSH_ASKPASS_SCRIPT
echo "\
#!/bin/fish
echo \"$argv[1]\"\
" > $SSH_ASKPASS_SCRIPT
chmod u+x $SSH_ASKPASS_SCRIPT
export DISPLAY="0"
export SSH_ASKPASS=$SSH_ASKPASS_SCRIPT
/usr/bin/keychain --clear id_rsa
rm $SSH_ASKPASS_SCRIPT
# Wait until WSL is ready
while(-not ((wsl --list --verbose) -replace '\u0000','' | Select-String -Pattern "Running" -SimpleMatch))
{
Start-Sleep -Seconds 5
}
Import-Module CredentialManager -Verbose
$credentials = Get-StoredCredential -Target sshpassphrase -Verbose
$BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($credentials.Password)
$passphrase = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
# If ssh-agent service startuptype is disabled, set it to manual
if ((Get-Service -Name ssh-agent | Select-Object -ExpandProperty StartType) -eq [System.ServiceProcess.ServiceStartMode]::Disabled)
{
Start-Process pwsh -Verb RunAs -ArgumentList @('-WindowStyle Hidden', '-NonInteractive', '-NoLogo', '-NoProfile', '-Command "Get-Service -Name ssh-agent | Set-Service -StartupType ([System.ServiceProcess.ServiceStartMode]::Manual)"')
}
# If ssh-agent service is not running, start the service
if ((Get-Service -Name ssh-agent).Status -ne [System.ServiceProcess.ServiceControllerStatus]::Running)
{
Start-Process powershell.exe -Verb runas -ArgumentList @('-WindowStyle Hidden', '-NonInteractive', '-NoLogo', '-NoProfile', '-Command "Get-Service -Name ssh-agent | Start-Service"')
}
# Hacky way to send password to ssh-add when -p is unsupported
$SSHAddInfo = New-Object -TypeName System.Diagnostics.ProcessStartInfo
$SSHAddInfo.FileName = Get-Command ssh-add | Resolve-Path
$SSHAddInfo.Arguments = "$env:USERPROFILE\.ssh\id_rsa"
$SSHAddInfo.UseShellExecute = $false
$SSHAddInfo.RedirectStandardInput = $true
$SSHAddInfo.CreateNoWindow = $true
$SSHAddProc = [System.Diagnostics.Process]::Start($SSHAddInfo)
Start-Sleep -Milliseconds 300 # Wait for ssh-add to start
Write-Debug "Sending password to ssh-add"
$passWithNewLine = $passphrase + "`n"
$SSHAddProc.StandardInput.WriteAsync($passWithNewLine)
$SSHAddProc.Close()
# <USERNAME> is the username on the default distro
C:\Windows\System32\wsl.exe -u <USERNAME> /home/<USERNAME>/wslu/keychain.sh $passphrase
C:\Windows\System32\wsl.exe -u <USERNAME> /home/<USERNAME>/wslu/keychain.fish $passphrase
#!/bin/bash
SSH_ASKPASS_SCRIPT=/tmp/ssh-askpass-script
cat > ${SSH_ASKPASS_SCRIPT} <<EOL
#!/bin/bash
echo "$1"
EOL
chmod u+x ${SSH_ASKPASS_SCRIPT}
export DISPLAY="0"
export SSH_ASKPASS=${SSH_ASKPASS_SCRIPT}
/usr/bin/keychain --clear id_rsa
rm ${SSH_ASKPASS_SCRIPT}
<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
<RegistrationInfo>
<Date>2022-01-21T12:31:37.6811474</Date>
<Author></Author>
<Description>Provide the SSH key password to WSL2 using ${SSH_ASKPASS_SCRIPT} environment variable read in by keychain in Linux.</Description>
<URI>\SSH to WSL Provider</URI>
</RegistrationInfo>
<Principals>
<Principal id="Author">
<UserId></UserId>
<LogonType>InteractiveToken</LogonType>
</Principal>
</Principals>
<Settings>
<DisallowStartIfOnBatteries>true</DisallowStartIfOnBatteries>
<StopIfGoingOnBatteries>true</StopIfGoingOnBatteries>
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
<IdleSettings>
<StopOnIdleEnd>true</StopOnIdleEnd>
<RestartOnIdle>false</RestartOnIdle>
</IdleSettings>
</Settings>
<Triggers>
<LogonTrigger>
<UserId></UserId>
</LogonTrigger>
</Triggers>
<Actions Context="Author">
<Exec>
<Command>C:\WINDOWS\System32\WindowsPowerShell\v1.0\powershell.exe</Command>
<Arguments>-NoLogo -NoProfile -NonInteractive -WindowStyle Hidden -File %userprofile%\Documents\keychain.ps1</Arguments>
</Exec>
</Actions>
</Task>
@mavaddat
Copy link
Author

mavaddat commented Jan 21, 2022

To install these scripts, use these commands in an admin PowerShell session:

<# Setup automatic ssh-agent #>
$DevPassword = Read-Host -AsSecureString -Prompt "Please enter your SSH password"
# Convert the SecureString to ClearText
$passClearBytes = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($DevPassword)
$passClear = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($passClearBytes)

# Check if wsl is installed
if ( -not (Get-Command wsl -ErrorAction SilentlyContinue -and ((wsl --list --verbose) -replace '\u0000', '' | Select-String -Pattern "Running" -SimpleMatch)))
{
    throw "WSL was not installed. Please start WSL and run the script again."
}

# Install keychain in WSL
Invoke-Command -ScriptBlock { Param($inputPass) wsl.exe --exec bash -c "echo $inputPass | sudo -S apt update && sudo -S apt upgrade -y && sudo -S apt autoremove -y && sudo -S apt install keychain" } -ArgumentList @($passClear)
# Download the helper scripts
wsl --exec bash -c "cd ~; git clone 'https://gist.github.com/a15fba958ee0f7d07043c8ade09959ec.git' 'keychainscripts'"
wsl --exec bash -c "cd ~/keychainscripts; chmod u+x keychain.sh keychain.fish; mv -t /mnt/c/Users/${env:USERNAME}/Documents keychain.ps1 SSHtoWSLTask.xml; mkdir ~/wslu; mv keychain.* ~/wslu/; cat .bashrc >> ~/.bashrc; cat config.fish >> ~/.config/fish/config.fish; rm -rf ~/keychainscripts"
# Set up scheduled task to hand-off the password to keychain
$taskXMLPath = "$env:USERPROFILE\Documents\SSHtoWSLTask.xml"
$keychainPs1Path = "$env:USERPROFILE\Documents\keychain.ps1"
$credTarget = 'sshpassphrase'
(Get-Content -Path $keychainPs1Path) -replace '<USERNAME>',"$env:USERNAME" | Set-Content -Path  $keychainPs1Path
$userSID = whoami /user | Select-String -Pattern "\s(\S+)" | Select-Object -ExpandProperty Matches | Select-Object -ExpandProperty Groups | Select-Object -Last 1 | Select-Object -ExpandProperty Value
$taskXML = [xml](Get-Content -Path $taskXMLPath)
Invoke-Command -ScriptBlock { $taskXML.Task.Triggers.LogonTrigger.UserId = "${env:COMPUTERNAME}\${env:USERNAME}" }
Invoke-Command -ScriptBlock { $taskXML.Task.RegistrationInfo.Author = "${env:COMPUTERNAME}\${env:USERNAME}" }
Invoke-Command -ScriptBlock { $taskXML.Task.Principals.Principal.UserId = $userSID }
Invoke-Command -ScriptBlock { $taskXML.Task.RegistrationInfo.Date = (Get-Date -Format "yyyy-MM-dd'T'HH:mm:ss.fffffffZ").ToString() }
$null = $taskXML.Save($taskXMLPath)  # Save the task to a file
schtasks /create /xml "$taskXMLPath" /tn "\SSH to WSL Provider" /ru "$env:COMPUTERNAME\$env:USERNAME"
Install-Module -Name CredentialManager   # Will store the ssh pass in cred manager
Import-Module CredentialManager -Verbose
New-StoredCredential -Target 'sshpassphrase' -UserName $env:USERNAME -SecurePassword $DevPassword

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment