Over SSH, can you use the same private key on the host side for other purposes?
I'm sat on one computer ('client') and connecting through SSH to another computer ('host') using my private key.
On the host computer, I would like to clone a Git repo using the private key that's stored on the client computer.
Is this possible? (Without copying the private key over to the host computer.)
Solution 1:
Theory
The functionality you seek is called "SSH agent forwarding". It works like this:
- There is an agent (a program, commonly
ssh-agent
) running in your local computer (or already forwarded from elsewhere). Other programs can communicate with the agent via a UNIX-domain socket, if only they know the path to the socket. Proper setup provides the right path. - You add keys to the agent on demand (
ssh-add …
), or programs likessh
add keys they use (if they are configured to do so). - Programs use the agent instead of using the key(s) on their own. In terms of this answer of mine we would say they ask the agent to solve puzzles for them.
- You use
ssh -A
locally to connect to an SSH server. Programs you then run on the server will be able to communicate with your local agent. Anything you run that knows how to talk to an agent or usesssh
under the hood will be able to use your local agent. The fact the agent is on another computer won't matter for them.
Starting an agent
There are few ways to start an agent:
-
Some systems are configured to start an agent when a user logs in. Each user gets their own agent. Your OS may have already started an agent for you. In a shell invoke:
env | grep '^SSH_AUTH_SOCK='
Non-empty output means the
SSH_AUTH_SOCK
variable is in the environment. The value of it is (or at least should be) the path to the UNIX-domain socket that allows programs to communicate with the already running agent. Programs find the socket by checking this exact environment variable. The existence of the variable normally means an agent is running and ready. A variable namedSSH_AUTH_SOCK
that doesn't point to the socket of your running agent is abnormal (something went wrong).If you see the variable then most likely an agent is there for you. Proceed to the next section of this answer.
-
You can start an agent by invoking
ssh-agent
in a shell, but don't do this directly. You want programs to be able to find the right variable in the environment. Solessh-agent
cannot alter the environment of its parent shell. It prints commands designed to be evaluated by the shell, so the shell itself changes its own environment. You can invokessh-agent
to see what it prints, but thenkill
the PID you learn from the output, otherwise the useless agent will keep running in vain.The right command is:
eval "`ssh-agent`"
There are few things that need explanation:
-
eval
can be dangerous, you may have heard that "eval
is evil". Here it's the right thing becausessh-agent
is one of few tools specifically designed to be used witheval
safely. -
Different shells require different syntax. What
ssh-agent
prints needs to match the shell. The tool prints shell code valid either insh
(and shells with similar syntax, e.g. Bash) or incsh
(and shells with similar syntax).ssh-agent
guesses the right style by examining theSHELL
variable; if it cannot guess then it will generate code forsh
. You can explicitly tell it what style you need by using-s
(forsh
-like shells) or-c
(forcsh
-like shells). The command will be like:eval "`ssh-agent -s`"
If you want to see the difference, invoke
ssh-agent -s
andssh-agent -c
withouteval
. Don't forget tokill
the right PIDs afterward. -
Nowadays we often use
$()
instead of backticks (and it's the right thing,$()
is better), but$()
doesn't work incsh
. This answer uses backticks to be equally useful insh
-like shells and incsh
-like shells.
After the
eval …
command,env | grep '^SSH_AUTH_SOCK='
should show you something. The agent will run until you kill it. The most proper way to kill the agent is:eval "`ssh-agent -k`"
in the same shell as before (use
-s
xor-c
if needed). Withouteval
the command is able to kill the agent but the variables will stay in the environment of the shell. No matter what you do in this shell, the variables will stay in the environments of child processes that inherited them and are still running. -
-
Yet another way of starting an agent is like this:
ssh-agent some_command …
The tool will start an agent and then run
some_command …
in the right environment.some_command
may bebash
, i.e. if you do:ssh-agent bash
then you will find yourself in an interactive Bash where
SSH_AUTH_SOCK
is already in the environment (note if your original shell is also Bash then it may seem the command did nothing; like invokingbash
from Bash may seem a no-op, until youexit
and find out you're still in Bash).The agent exits automatically when
some_command
terminates, no maintenance is needed. This method is quite straightforward.If you need an agent only for one local
ssh
thensome_command
may be thessh
. In your case you needssh -A
:ssh-agent ssh -A …
Depending on what key(s) you want to use and the configuration of
ssh
, the above may or may not be a good idea. In some circumstances you may want to do something with the agent before you runssh -A
(like adding keys, see below).
Adding keys to an agent
When an agent starts, it does not have any private keys. If you find an agent started by your OS, it's theoretically possible the OS not only started the agent but also added some key(s). Keys are added using ssh-add
or by ssh
when AddKeysToAgent
is set in ssh_config
.
In a shell with access to an agent (SSH_AUTH_SOCK
in the environment, the agent running) invoke:
ssh-add -l
to see keys (identities) represented by the agent. There may be none. To add a key run:
ssh-add path/to/key
Note ssh-add
without arguments probes some standard pathnames and adds files it finds. See man 1 ssh-add
for details.
If AddKeysToAgent
is set in ssh_config
(by default it's not) or if you request it in the command line, ssh
itself will add the key it uses. This means if you want to use the same key to authenticate to the server and later on the server then you don't need ssh-add
. You can do:
# with agent already running
ssh -A -o AddKeysToAgent=yes …
This works with keys specified with -i
as well. These are the circumstances that allow you to run an agent and ssh
in one command (the method mentioned at the end of the previous section):
# already running agent (if any) is irrelevant
# we are starting a new agent
ssh-agent ssh -A -o AddKeysToAgent=yes …
Invoking ssh
The relevant option is -A
. From man 1 ssh
:
-A
Enables forwarding of connections from an authentication agent such asssh-agent(1)
. This can also be specified on a per-host basis in a configuration file.
(The keyword in the configuration file is ForwardAgent
.)
The server may or may not allow forwarding. See AllowAgentForwarding
and DisableForwarding
in man 5 sshd_config
.
Be warned:
Agent forwarding should be enabled with caution. Users with the ability to bypass file permissions on the remote host (for the agent's UNIX-domain socket) can access the local agent through the forwarded connection. An attacker cannot obtain key material from the agent, however they can perform operations on the keys that enable them to authenticate using the identities loaded into the agent. […]
If the server allows forwarding and the right key has been added to the local agent then -A
is all you need to add to the ssh
command you use to connect to the server. It may be as easy as:
ssh -A user@server
And with ForwardAgent yes
in ssh_config
(or in your ~/.ssh/config
) you don't even need -A
in the command line, pure ssh user@server
will do.
Situation on the server
If your local ssh -A …
gives you an interactive remote shell then you can use the same command as we used earlier (locally), now on the server:
# on the server
env | grep '^SSH_AUTH_SOCK='
If the forwarding works then you will find the variable set. It's as if ssh-agent
was running, but it's really the SSH server who listens on the socket and relays to your local ssh-agent
. Programs that use the socket don't care. You can even forward the agent further (with ssh -A
from the server to yet another computer).
Simply use ssh
or another command able to talk to an agent. It should find the socket and talk to the local ssh-agent
.
ssh-add
is no different. It's not obvious, but if you use ssh-add
on the server to add some key then the tool will actually add the key from the server (rather than telling your local ssh-agent
to open a local file with the same path). You can disconnect from the server and your local agent will keep holding the added key. So a local ssh-agent
not only allows you to use your local key on the server; it also allows you to use your key from the server to connect from your local to wherever. There's a big difference though. ssh-agent
needs to store some representation of added keys, if only in memory. Local ssh-add
talking to a local ssh-agent
does not make the local key leave the local computer. Remote ssh-add
talking to a local ssh-agent
copies the remote key to local memory.
If your local ssh -A …
specifies a command to run on the server (as opposed to an interactive shell), nothing will change in the mechanics of agent forwarding. The remote command will find SSH_AUTH_SOCK
in the environment and it will be able to talk to your local ssh-agent
.
Possible problems (already solved), interesting cases
- How do I get
ssh
to use only one key from ssh-agent? - SSH always using first key accepted by server
Documentation
man 1 ssh-agent
man 1 ssh-add
man 1 ssh
man 5 ssh_config
man 5 sshd_config
Other
- Guide on GitHub