Basically, I'm trying to let the user use a private key without having read access to it.

Use case: Employee needs to SSH to a server in the data center. There is 1 private key for all servers, stored on the bastion. How can the employee use the bastion as a jump box while still protecting the private key?

  1. Employee connects (over SSH) to bastion.
  2. Bastion host uses jailed shell to lock down that employee's access.
  3. Employee then SSH connects to an end-server (but didn't touch the private key in the process)

Is there an application/tool/script/method for the bastion to provide SSH access to another server without disclosing the private key to employee?

One thought I had is that there might be a way for a listener on the bastion to trigger its own SSH connection to the end node, and then provide the jailed user a shared 'screen' or shared shell to facilitate the access to the end-node?

Maybe I'm over complicating this and there is a better solution out there. Per-user private keys seem like a nightmare to manage.

  • 2
    Let me see if I've got this right: you're trying to give the user access to a private key (for the purposes of SSH) without giving them access to the private key?
    – Mark
    Commented Jul 6, 2014 at 23:39
  • As crazy as that sounds, yes. Essentially, I'm wishing there is a way for the bastion to authenticate to the end-node and hand off the shell to the employee. Commented Jul 7, 2014 at 0:32
  • 1
    Can you explain why you want to do this? I can have a guess (e.g. avoid changing keys when an employee leaves) - but it would help to understand exactly what you're trying to achieve. Also, once the users SSH to the target server, are they logged in as root?
    – paj28
    Commented Jul 7, 2014 at 12:42
  • 1
    The SSH keys should still be changed when an employee leaves as a precaution, but I want to do it this way to ensure that if they are compromised by an attacker, that the attacker can't even read the SSH key. The current end-node login is not root. Commented Jul 7, 2014 at 13:39
  • 1
    Is this not what host authentication is all about. You allow only the hosts with certain keys to connect to the server. Presuming all users on the host are allowed to connect (otherwise why are you allowing them on the Bastion host)
    – user60618
    Commented Nov 11, 2014 at 17:10

5 Answers 5


A possibility would be to use 'sudo' (or a script / alias relying on it).

Thanks to sudo, you can allow your users to temporarily use another (not necessarily root) in order to execute a very specific command:

  1. Create a new account which will own the private key,
  2. Configure sudo so your users can launch an SSH client using this account and its private key.

By this way your users will be able to run an unprivileged SSH client using a private key, without being allowed to access directly the private key itself.

  • If they can sudo, could they not just 'sudo cat' the private key? Commented Jul 7, 2014 at 13:37
  • 4
    That's precisely the big improvement between 'sudo' and the older 'su'. While with 'su' you can allow certain people to use another count, you have no mean to control which command they would use. With 'sudo' on the contrary you can put a very restrictive control over the exact command they can use. For instance, you can allow them to launch only "sudo ssh -i $HOME/.ssh/secretkey myhost" (with or without password, as your convenience), any other command like "sudo cat" would then be forbidden. Commented Jul 7, 2014 at 13:49
  • More information about sudo configuration can be found here Commented Jul 7, 2014 at 13:56
  • 1
    As a complementary information, I already used such a method on a project to grant some users with limited administration capabilities (actually add new users, without having full administrative right on the host). This works great, the only pitfall is that you must ensure that the command allowed does not offer a way to escape to a local shell. For instance, allowing to launch 'vi' through sudo is not a good idea since one can then ask vi to launch a local shell using current account (:!sh), same issue with ftp client (!sh), but ssh client seems safe. Commented Jul 7, 2014 at 14:23
  • That makes sense, thank you! Where do I define that a user can only launch "sudo ssh -i $HOME/.ssh/secretkey myhost" ? Commented Jul 7, 2014 at 15:02

1: Use hardware tokens, like a Yubikey configured for challenge-response based authentication. Or smartcards. You load up the key on them all and hand them out. They're designed to keep the secrets secret.

2: Stop using a single key, start using one keypair per user for accountability and practical revocability.


WARNING: Creativity ahead, which is often bad for security (at least without thorough review).

This sounds like a case in which an SSH agent could be useful. An SSH agent provides a socket interface over which SSH clients can ask the agent to perform key operations for them, which enables the following common uses:

  • You can have the long-running agent decrypts your private key once, instead of having each client decrypt the key separately.
  • You can have your SSH client forward the agent connection, allowing you to use your key from the remote host without actually storing it there or otherwise revealing it.

This second case in particular sounds a lot like your use case, except instead of granting access on a different host, you want to grant access to multiple users on the same host.

It would look something like this:

sshkeeper@bastion$ eval "$(ssh-agent -a /path/to/socket)"
sshkeeper@bastion$ echo $SSH_AUTH_SOCK

sshkeeper@bastion$ ssh-add
Enter passphrase for /home/sshkeeper/.ssh/id_rsa:

sshkeeper@bastion$ chmod 660 /path/to/socket
sshkeeper@bastion$ chgrp ssh-users /path/to/socket

The key doesn't necessarily need a passphrase; I included one for illustration. In any case, now anyone with read and write permissions to the socket can use the...

jander@bastion$ export SSH_AUTH_SOCK=/path/to/socket
jander@bastion$ ssh private_server
Error reading response length from authentication socket.
Permission denied (publickey).

Oops. It's a little trickier than that, because ssh-agent is paranoid and double-checks the user ID of whoever connects to the socket. So we need a workaround. Let's try using socat as a proxy:

sshkeeper@bastion$ eval "$(ssh-agent -a /home/sshkeeper/ssh_agent_socket)"
sshkeeper@bastion$ ssh-add
Enter passphrase for /home/sshkeeper/.ssh/id_rsa:
sshkeeper@bastion$ rm /path/to/socket
sshkeeper@bastion$ umask 117
sshkeeper@bastion$ socat UNIX-LISTEN:/path/to/socket,fork,group=ssh-users UNIX-CONNECT:/home/sshkeeper/ssh_agent_socket &

Now it's the sshkeeper-owned socat process that's connecting to the agent, and not the jander-owned SSH client, so the agent has no complaint:

jander@bastion$ export SSH_AUTH_SOCK=/path/to/socket
jander@bastion$ ssh private_server
jander@private_server$ █

You'll want to be careful of the filesystem permissions for accessing the socket, as that's all that's left to protect it. Don't give anyone but sshkeeper write access along the path to the socket. Beware also that some BSD-derived systems ignore the permissions on the socket file itself; test this thoroughly on your system.

Finally, beware that this is a nonstandard use of the SSH agent: in fact I had to work around a security feature to make it work. I believe that on a Linux system with proper filesystem permissions, it can be at least as safe as or safer than the sudo solution... but you shouldn't take my word for that. Make sure you understand exactly what's going on here before using this.

UPDATE: I've taken a look at the openssh ssh-agent source code to see what you can do with an agent socket, and I can see a couple of opportunities for minor mischief. Namely, your users can ask the agent to add or remove keys.

So a person could tell the agent to drop the key, locking everyone else out of the servers until you (or a script) re-add the key.

A person could also add a key, giving everyone else access to additional servers. However, this person could also hand other people the key outright (without using the server), or use their own shared agent, so this point is relatively minor.


Well besides the sudo answer (which is really clever btw), another solution is a restricted shell. In this case you would have to write one. In this case the only commands you need to accept are "ssh <hostname>" and "exit" so it's not so hard.

I'm really only posting this for completeness, but the general technique is valuable in other contexts.


Use host-based authentication

While generally unknown, ssh supports authenticating the remote machine, much in a similar way to rsh (but more secure).

There's a good manual on https://en.wikibooks.org/wiki/OpenSSH/Cookbook/Host-based_Authentication

The bastion ssh server would have a private key that is accessed through the setuid ssh-keysign binary (the user doesn't get access to that), and the remote servers would be configured to simply trust anyone coming from the bastion host (you decide if that's wise or not).

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .