Bastion Server – TCP Forwarding vs Placing Private Key on Server

bastionlinuxprivate-keySecurityssh

We have bastion server B.
We need to SSH from A through B to C, using private key.

What is the better option:

  • Put the private SSH key on server B. We read that it's a bad idea to do that in a production environment.

    From here:

    Never place your SSH private keys on the bastion instance. Instead,
    use SSH agent forwarding to connect first to the bastion and from
    there to other instances in private subnets. This lets you keep your
    SSH private key just on your computer.

  • Use SSH agent forwarding. For setting up agent forwarding, I need to allow TCP Forwarding. When setting up agent forwarding, a socket file is created on the forwarding host, which is the mechanism by which the key can be forwarded to the destination. In the Bastion settings at AWS:

    TCP forward: Setting this value to true will enable TCP forwarding
    (SSH tunneling). This can be very useful but it is also a security
    risk, so we recommend that you keep the default (disabled) setting
    unless required

    Also from here:

    SSH Agent Forwarding considered harmful

What is better? What about the alternative from the second link: ProxyCommand, I understand it helps with the socket file issue, but still I think I have to enable TCP forwarding, so is it secure enough?

Best Answer

Use ProxyCommand or ProxyJump

I would recommend to use ProxyCommand (or even better ProxyJump as the syntax is easier but requires openssh 7.3+ I think on the client side), and you do not need to deploy private key on the Bastion, everything stays local.

Example with ProxyJump

On your client computer you write a file under ~/.ssh/config with a similar content to bellow:

Host bastion
  HostName bastion.example.com
  User bastion-user
  Port 22
  IdentityFile ~/.ssh/id_bastion

Host srvC
  HostName srvC.local
  User server-user
  IdentityFile ~/.ssh/id_protected_lan
  ProxyJump bastion

Then doing ssh srvC will connect you to C via B (bastion) without Agent Forwarding nor deploying the private key to the bastion.

In the above example, "bastion" is an alias to your Bastion host and srvC is an alias to your server C. In the HostName you need to put either IPs or real fully qualified domain name for your hosts. For the users, you need to update the User for the correct login name on the Bastion and server C. Finally the IdentityFile is optional if you use a local agent (e.g. KeeAgent or ssh-agent), but if it is not running then it will also work and ask you for each key passphrases.

Deploying the public keys

Of course you need to deploy the public keys to both bastion and srvC. You can use (the $ sign is just to illustrate the prompt, do not type it):

$ ssh-copy-id -i ~/.ssh/id_bastion.pub \
   -o PreferredAuthentications=password \
   -o PubkeyAuthentication=no \
   bastion
$ ssh-copy-id -i ~/.ssh/id_protected_lan.pub \
   -o PreferredAuthentications=password \
   -o PubkeyAuthentication=no \
   srvC

Note: the above will work only if password authentication is still allowed. After the above deployment and verifying that everything work as intended, you should disallow password authentication on the 2 servers.

Example with ProxyCommand instead of ProxyJump

If you have an older version of OpenSSH which does not support ProxyJump (on the client side), then replace:

ProxyJump bastion

by

ProxyCommand ssh -q -W %h:%p bastion

As far as I understood, this is similar.