Everyone agrees that it’s a bad idea to use the SSH protocol’s HostbasedAuthentication as the sole method for authenticating remote logins. I’m not convinced that it’s a bad idea for the purposes of providing multi-factor authentication. It might even be okay.
Say you manage a fleet of mobile workstations (*nix laptops), with users who don’t need local or remote superuser access. They use the workstations to authenticate via SSH from anywhere in the world to servers you control. Were a user account on one of these workstations to be compromised, an attacker could keylog passwords or steal user pubkeys, then use them from their own endpoints. Enforcing password or pubkey authentication along with hostbased authentication would guarantee that the attacker wouldn’t be able to use these stolen passwords or pubkeys outside of the compromised machine.
This is an admittedly narrow use case, but it would mean that the compromise of my unprivileged user session would require that the attacker dwell within an active, local user session to use them, or be able to return to the host. Either situation increases the likelihood of the attacker getting caught (especially with EDR), and makes my closing the lid to my laptop inconvenient for the attacker.
Hostbased authentication is available with every distribution of OpenSSH I’ve encountered. The project’s release notes make it sound as though it’s been supported since SSHv2 support was added in OpenSSH 2.1.0p1, released 2000–05–09.
It allows for the assigning of credentials to both a user and a device, rather than just the user. This means that the IT shop in an organization can provision a user’s workstation with a host SSH key pair, and so long as the user never has permission to read the private key, compromise of the user’s account on that system can’t lead to the exfiltration of the host SSH key.
In theory, the attacker would have to stay on the compromised machine in order to access servers to which the user has access.
It’s free!
Hostbased authentication was designed to be a single-factor authentication method for low-security sites (RFC 4252 § 9), and when the client is on a network you control. To fit our use case, you have to turn off the default behavior of performing a reverse-lookup on the IP address of the connecting client to make sure it matches the name of an allowed host, and rely on the host SSH key to authenticate the connecting endpoint. Turning off a safety feature never sits well with me, but in my view, it’s the client pubkey that’s really authenticating the client, not the PTR record for its address.
It might not work for Windows clients using PuTTY, as I have yet to find any articles documenting PuTTY’s use with hostbased auth. Windows users might be better served by the Linux subsystem for Windows, but that’s a complete guess on my part. It’s not available in the SSH client app I have on my smartphone.
It won’t help if the user is an admin on either the client or the server. If they have superuser access on the client, their compromised account could read (and steal) the host key. Of they have superuser access on the server, they could edit sshd_config, shosts.equiv, and ssh_known_hosts to permit a host key supplied by the attacker.
As HostbasedAuthentication is a bad idea for single-factor auth, not many people use it, making it an odd-ball. Odd-ball implementations are always suspect.
It’s “free”. The (F)OSS devotee in me listed this as a pro, but the professional in me knows it will carry the cost of staff time to design, implement, and troubleshoot. For example:
You have to manage the creation of keys on client workstations.
You have to manage the distribution of host keys to servers, so the servers know which client hosts to trust.
You have to remotely troubleshoot a separate layer of authentication when something goes wrong.
Implementation for an unprivileged user on the client side accessing an account on the server side from any IP address:
Client
SSH host key files in /etc/ssh, readable only to root.
/etc/sshd_config permits HostbasedAuthentication and EnableSSHKeySign
ssh-keysign is set uid root
ssh client invokes ssh-keysign when user connects to server. Server sends client a message to sign.
ssh client signs the message with one of the client’s SSH host keys using ssh-keysign.
Server
HostbasedAuthentication turned on in sshd_config, either globally (bad idea) or explicity in a Match statement on a user or group (good idea), like this:
HostbasedUsesNameFromPacketOnly yes # disables DNS reverse lookup
HostbasedAuthentication yes
AuthenticationMethods “publickey,hostbased”
Client’s SSH host pubkeys are listed in /etc/ssh/ssh_known_hosts
/etc/shosts.equiv has a list of client hostname and usernames permitted to authenticate from the client.
This presumes you have superuser access on a client system (i.e. your laptop) and on a remote system listening for inbound SSH connections. Both are configured with a unprivileged user that will log in to the server from the client.
Prepare the client system.
As the superuser, run ssh-keygen -A to create host keypairs for each of the supported algorithms in /etc/ssh/, using the default key length for each algorithm.
As the superuser, edit /etc/ssh_config to permit use of Hostbased authentication by adding the lines:
“HostbasedAuthentication yes”
“EnableSSHKeysign yes”
Prepare the server system
On the client system, use scp to copy all of the host key files /etc/ssh/ssh_host_*.pub to the server.
On the server, concatenate all of the host key files into /etc/ssh/ssh_known_hosts
On the server, edit /etc/ssh/ssh_known_hosts so that each line starts with the client’s hostname. In traditional Hostbased auth scenarios, where it’s being used as a single authentication factor, this is meant to be an FQDN that must match the PTR record for the IP address from which the client is connecting. As we’ll configure it here, though, it will be whatever hostname the ssh client application will report to the server. For example:
samslaptop.local <begin host key material>
samslaptop <begin host key material>
On the server, /etc/shosts.equiv should have lines of the form <remote host> [-] <remote username>
, where:
<remote host> should match the host names listed in /etc/ssh/ssh_known_hosts
Lines without remote usernames will allow all users on the remote machine to log in as any user on the server, except root
Alternatively, if you want a user on a client to log in as a different user on the server, you need a ~/.shosts file
Note: you can’t allow root login in /etc/shosts.equiv. (ssh(1)) And you shouldn’t be allowing remote root logins!
On the server, in /etc/ssh/sshd_config:
IgnoreHosts shosts-only
in a user Match statement so a ~/.shosts file can be set for it.On the server, in /etc/sshd_config:
Match User tester
IgnoreRhosts shosts-only # Optional, if you want there to be per-user ~/.shosts files for this user.
HostbasedUsesNameFromPacketOnly yes
HostbasedAuthentication yes
X11Forwarding no # Just a precaution
AuthenticationMethods “publickey,hostbased” # Requires both methods for this user
To troubleshoot this nonsense server-side, I set LogLevel DEBUG3
in sshd_config, and look at messages in these logfiles:
On the client, I just used ssh -vvv
while connecting.
The process for deprovisioning and distributing host keys could be compromised to:
Remove host keys from servers or clients, bricking user access.
Add host keys from unauthorized clients to servers, defeating the “zero trust” nature of the deployment.
The hostkey could be generated with a method later determined to be weak. This is a risk with pubkeys generally, but a friend pointed out that keys can stick around for a long time, and a situation like the 2008 Debian weak keys issue could always happen again.
If the user on the client is an admin on the server, a malicious user could use superuser privileges to either lift the Hostbased authentication requirement for the user account on the server, or install host keys for a differnet client machine they control. I went down the rabbit hole of thinking of ways to use mandatory access control policies to prevent the superuser from modifying the files that support all of this, and concluded that you could only ever just fool yourself into thinking you’d gotten it right. Every scenario I can think of would either leave some path for the superuser to make malicious changes, or make the system hard to manage remotely.
Security keys like the YubiKey 5 Nano.
Pro: easier to use and deploy
Cons: can be used from machines you don’t control
Push-notification MFA solution
Pro: easier to use and deploy
Cons: “phushable” through notification fatigue.
As the list of “cons” above is longer than the list of “pros”, I’ve pointed out a number of ways this could go wrong if not done precisely, it only addresses a very narrow use case, and I haven’t found anyone on the web describing this set-up, I wouldn’t recommend it for production use. Still, I’m trying this out on a client and server I own to see how practical it is and to try to catch a nasty edge case.
Also, while I said above that you could use Hostbased authentication with passwords or per-user pubkeys, I was just being polite. No one should use passwords.
A previous version of this page stated that setting IgnoreRhosts to “no” in /etc/sshd_config would prevent the use of per-user ~/shosts files. That has been corrected. In this setup, you’d want to leave it on the default setting of “yes” in the global config, and “shosts-only” on a per-user or group basis in a Match statement. See the discussion above in the section “Procedure”.
A previous version of this page showed me disabling TCP forwarding in sshd_config on the server as a precaution against misuse of the service by a connecting client. My aim was to disallow the connecting user the ability to use the SSH connection as a tunnel for other traffic, as described well in How to Set up SSH Tunneling (Port Forwarding). Re-reading sshd_config(5)), I concede that this option doesn’t protect your server if the connecting user has shell access, as the user can then just install their own forwarder. If you want to prevent users with shell access from engaging in this behavior, I think some form of monitoring might be your best bet, which would be an interesting topic for another blog post.