Persistent ssh-agent on Bash on Ubuntu on Windows
Posted by Dave Eddy on Oct 18 2017 - tags: techAfter installing Bash on Ubuntu on Windows I realized some interesting side effects related to how processes and daemons in the Unix environment are handled. Running a process in the background, or daemonizing a process, will work so long as there is a Bash session open on Windows. Once the last window is closed, all of the processes are cleaned up and killed.
I use ssh keys for authentication when connecting to remote servers which
requires the use of ssh-agent
. I can run this program manually and it will
work so long as there is at least one bash session running on my computer, but
once I close the last window the ssh-agent
is killed and my keys are
unloaded. I’ve found a couple guides online regarding ssh-agent
and WSL
specifically, but most of them assume the keys you are using are not password
protected.
To remedy this situation, I managed to find a way to create a hidden terminal
session that runs ssh-agent
in foreground mode when I login to my computer
which persists through sleeps and hibernations. This way, ssh-agent
will run
and stay running from the moment I login until the moment I logout (which is
almost never, unless I reboot).
Update
I received an anonymous email tip to simplify this entire process - no bat
or
vbs
scripts required!
Basically, install the start-ssh-agent
bash script outlined below into
~/bin
, and then create the scheduled task to execute the following line with
the same rules.
powershell -noprofile -windowstyle hidden -command "c:\windows\system32\bash.exe -c "~/bin/start-ssh-agent""
And that’s it! The GitHub repo has been updated to reflect this new process as well.
https://github.com/bahamas10/windows-bash-ssh-agent
Deprecated Process (for historical purposes)
Create the Scripts
The short version of this is a VBS script is executed by the Windows Task
Scheduler at login, which runs a .bat script in a hidden terminal window, which
calls bash, which calls a bash script, which calls ssh-agent
if it is not
already running.
Task Scheduler
-> start-hidden.vbs
-> start-ssh-agent.bat
-> bash
-> start-ssh-agent
-> ssh-agent
It sounds confusing, and it unfortunately is, but it works!
To get started, I created a symlink in my Unix home directory that maps to my home directory on Windows to simplify things.
ln -s /mnt/c/Users/dave/ ~/userprofile
Now, I can access ~/userprofile
which is a link to my users folder on
Windows. I created a directory for scripts in My Documents and ~/bin
using
the following commands.
mkdir ~/bin
mkdir ~/userprofile/Documents/scripts
Note: all of the scripts below can also be found on GitHub
https://github.com/bahamas10/windows-bash-ssh-agent
start-hidden.vbs
This script will call the bat script in a hidden command prompt window
~/userprofile/Documents/scripts/start-hidden.vbs
Dim WinScriptHost
Set WinScriptHost = CreateObject("WScript.Shell")
WinScriptHost.Run Chr(34) & "%userprofile%\Documents\scripts\start-ssh-agent.bat" & Chr(34), 0
Set WinScriptHost = Nothing
start-ssh-agent.bat
This script will call a bash script that’s responsible for starting ssh-agent
if it is not already running. Note that -c
is required with bash so ~
gets
resolved.
~/userprofile/Documents/scripts/start-ssh-agent.bat
\Windows\System32\bash.exe -c "~/bin/start-ssh-agent"
start-ssh-agent
Finally, this is the meat of the logic here. This bash script will start
ssh-agent
if it is not already running.
~/bin/start-ssh-agent
#!/usr/bin/env bash
#
# Start ssh-agent if it is not running (by becoming it)
# Intended for use on Bash for Windows using the WSL
#
# Author: Dave Eddy <[email protected]>
# Date: October 09, 2017
# License: MIT
# Could be any file - nothing intrinsically valuable about ~/.ssh/environment
envfile=~/.ssh/environment
# Ensure the environment file exists and has its permissions properly set.
# Source the file - if it was created by this script the source will
# effectively be a noop.
mkdir -p "${envfile%/*}"
touch "$envfile"
chmod 600 "$envfile"
. "$envfile"
# Check if the daemon is already running
if [[ -n $SSH_AGENT_PID ]] && kill -0 "$SSH_AGENT_PID" 2>/dev/null; then
# The PID is up but it could have been recycled - attempt to list keys.
# This will exit with 2 if the SSH_AUTH_SOCK is broken.
ssh-add -l &>/dev/null
if (($? != 2)); then
echo "alreading running: $SSH_AGENT_PID"
exit 1
fi
fi
# Overwrite what is in the envfile to start a fresh ssh-agent instance
echo "# Started $(date)" > "$envfile"
# For some reason, this line doesn't get emitted by ssh-agent when it is run
# with -d or -D. Since we are starting the program with exec we already know
# the pid ahead of time though so we can create this line manually
echo "SSH_AGENT_PID=$$; export SSH_AGENT_PID" >> "$envfile"
# Become ssh-agent and run forever
exec ssh-agent -D >> "$envfile"
Install the Service
The next step is to have Windows start the ssh-agent
when you login. To do
this, open the task scheduler by opening the start menu and searching for “Task
Scheduler” and opening the main result.
- Click
Create Basic Task...
on the right - Enter a name like
ssh-agent bash
- Set Trigger to
When I log on
- Set Action to
Start a program
- Set Program/script to
C:\Users\dave\Documents\scripts\start-hidden.vbs
- Finish up
The next thing to do is modify the tasks conditions and settings.
For the conditions, uncheck pretty much everything. Basically, we want this service to start regardless of battery/AC state.
For the settings, uncheck pretty much everything again except for allowing the task to run on demand.
This service will now run when you login to the computer, but for now you can click the newly created task and select “Run” on the right to start it manually this once.
Update your bashrc
The final step is to have your Windows bashrc file source the ssh environment
file that is written by the start-ssh-agent
script.
echo '. ~/.ssh/environment > /dev/null' >> ~/.bashrc
Here is the section in my bashrc that actually detects if I’m on WSL to then source in the env file.
https://github.com/bahamas10/dotfiles/commit/fd7047243293674ed38f69ce5653104373ac727b
Done!
Now, whenever you open a bash shell ssh-agent
will be running and its
required environmental variables will be set for you to use. You can verify it
is running using ssh-add -l
.