Limiting Exposure via ssh ProxyJump

ssh is an amazingly prolific tool that is used extensively by anyone who manages systems. It’s a tool that many of us trust to provide the ultimate command and control access to devices we manage, and on many commercial systems it can be marginalized by being updated infrequently. If you’re able to run modern OpenSSH you have access to a new feature named ProxyJump, which makes using a jumphost much simpler.

Motivation

The idea of a jumphost has been around for a while. It allows you to utilize a bastion device at the edge of the network to jump through to devices that you’d prefer don’t have public routes to them.

Home Networks: One case that I see relatively often is people wanting to ssh back into their home network. Many of my friends/family now use Ubiquiti devices because I’ve tried to create some conformity among inexpensive networking devices. With the EdgeRouter platform you have Ubiquiti’s version of Debian under the hood, so you can set up a firewall rule to allow you to ssh into the router, at the edge of your network, then ssh further into your network. This is very bad though, because the EdgeRouter doesn’t receive updates to its core packages very often, and there is a homogeneous attack surface for people to target (e.g. compromise one EdgeRouter and you can attack all of them on the internet).

In this case it is much better to get a device that is updated independently of a corporate entity to run your sshd on. I typically chose Odroid or RasberryPi which have ArchLinuxArm installs available for them. You can provision these devices with the most up to date upstream version of OpenSSH and feel good about you likely being ahead of where wide sweeping targeted attacks would occur. You’d obviously need to port forward from the router to this device.

A side note, sshuttle is amazing for home networks.

Server Groups: Another case that is coming up often is when you have a set of servers operated by the same host. Vultr, like many other providers, provides a way to do private local networking between hosts that you allocate in their environment. You can simply set up a single host to be your sshd target from outside networks, then make sure that the other servers are listening on the private interface for their sshd implementations.

ProxyJump

Starting in openssh 7.3 the ProxyJump directive became available:

  • ssh(1): Add a ProxyJump option and corresponding -J command-line flag to allow simplified indirection through a one or more SSH bastions or “jump hosts”.

Before ProxyJump there were a variety of methods to accomplish the same task, and users would have to make trade-offs based on the network limitations or jump server capabilities. If you used port forwarding instead of passing the connection to stdout on the jump server, then your connection was considered secure because the encryption stays end-to-end. If using the stdout method you would be forcing decryption on the jump server, then re-encrypting on a new connection between the jump server and the next host.

For the port forwarding methods you’d commonly see ProxyCommand or the -L directive being used to set up port forwarding between devices. ProxyJump is just a new abstraction over the port forwarding principle that is overall syntactically easier to use.

In my example I’ll be using an Odroid C2 sitting at 172.16.0.8, behind an EdgeRouter, with port forwarding set up. I have a port forward rule on the EdgeRouter as follows:

port-forward {
    auto-firewall enable
    hairpin-nat enable
    lan-interface eth7.7
    rule 1 {
        description arbiter
        forward-to {
            address 172.16.0.8
            port 8888
        }
        original-port 8888
        protocol tcp
    }
    wan-interface eth0
}

For reference, this is the /etc/ssh/sshd_config on the jump server:

# from https://cipherli.st/ and https://wiki.mozilla.org/Security/Guidelines/OpenSSH
Port 8888
Protocol 2
HostKey /etc/ssh/ssh_host_ed25519_key
HostKey /etc/ssh/ssh_host_rsa_key
KexAlgorithms curve25519-sha256@libssh.org,diffie-hellman-group-exchange-sha256
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-ripemd160-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-512,hmac-sha2-256,hmac-ripemd160,umac-128@openssh.com
AuthenticationMethods publickey
LogLevel VERBOSE
Subsystem sftp  /usr/lib/ssh/sftp-server -f AUTHPRIV -l INFO
PermitRootLogin No

Now, from my laptop I want to set up a Host definition in my ~/.ssh/config file:

Host arbiter.network
    HostName network.tld
    Port 8888

And, a separate Host definition for a device within that network that I want to get to through my jump server:

Host inside.network
    HostName 172.16.0.10
    ProxyJump arbiter.network

Now when I type ssh inside.network I will be jumping through arbiter.network to get to inside.network.

Now I don’t have to worry about someone scanning my network and seeing a very old version of OpenSSH being exposed on my router. Also, the jumphost has literally no information on it that allows for ratcheting into the network, other than allowing it to route to machines. This machine can be slapped from the internet all day long and there is little worry. One could even consider rate limiting with the firewall, or using a log based firewall updating tool such as sshguard.

To really get things locked down one might use a VPS (or similarly inexpensive server) to be their jumphost so that the site firewalls only allow inbound connections on the port ssh is using from a single address.