OpenSSH/Cookbook/Host-based Authentication

 

Host-based authentication allows hosts to authenticate on behalf of all or some of that particular host's users. Those accounts can be all of the accounts on a system or a subset designated by the Match directive. This type of authentication can be useful for managing computing clusters and other fairly homogeneous pools of machines.

In all, three files on the server must be prepared for host-based authentication. On the client only two must be modified, but the host itself must have SSH host keys assigned. What follows sets up host-based authentication from one system to another in a single direction. For authentication both directions, follow the procedure twice but reverse the roles of the systems.

Client-side Configurations for Host-based Authentication

edit

On the client or source host, two files must be configured and in addition at least one host key must exist:

/etc/ssh/ssh_known_hosts - global file for public keys for known hosts
/etc/ssh/ssh_config - allow clients to request host-based authentication

Then the client's host keys must be created if they do not already exist:

/etc/ssh/ssh_host_ecdsa_key
/etc/ssh/ssh_host_ed25519_key
/etc/ssh/ssh_host_rsa_key

These three steps need to be done on each of the client systems which will be connecting to the specified host. Once set up, the accounts logged on one system will be able to connect to another system without further interactive authentication.

Note that in situations environments host-based authentication might not be considered sufficient to prevent unauthorized access since it goes on a host-by-host basis, mostly.

1. Populate the Client with the Server's Public Keys

edit

The remote server's public host keys must be stored on the client system in the global client configuration file /etc/ssh/ssh_known_hosts. One way to get the public keys from the server is to fetch them using ssh-keyscan(1) and save them.

$ ssh-keyscan server.example.com | tee -a /etc/ssh/ssh_known_hosts

Be sure to make a backup copy of /etc/ssh/ssh_known_hosts, if it exists, before trying anything. Another way would be to add the server's public host key to the ~/.ssh/known_hosts files in the relevant accounts. But that way is more work.

However they are acquired and verified, the public keys listed there must obviously correspond to the private host keys on the server. Any or all of the three types can be used, RSA, ECDSA, or Ed25519. DSA should no longer be used. For verification of the public keys, refer to the section on how to verify a host key by fingerprint for some relevant methods.

2. System-wide Client Configuration

edit

Two changes must be made. First, the client's configuration must request host-based authentication when connecting to the designated systems. Second, the client configuration file must be set to enable ssh-keysign(8). It is a helper application to access the local host keys and generate the digital signature required for host-based authentication. Both changes can be done globally in /etc/ssh/ssh_config.

Here is an excerpt from /etc/ssh/ssh_config on the client trying host-based authentication to all machines. The Host directive in ssh_config(5) can be used to further constrain access to a particular server or group of servers.

Host *.pool.example.org
	HostbasedAuthentication yes
	EnableSSHKeysign yes

In some distributions, such as Red Hat Enterprise Linux 8, the EnableSSHKeysign directive may need to be placed into the general section before it will work. In that case, do this:

Host *.pool.example.org
	HostbasedAuthentication yes

Host *
	EnableSSHKeysign yes

Other configuration directives can be added as needed. For example, here are two additional settings applied to the same pool:

Host *.pool.example.org
	HostbasedAuthentication yes
	EnableSSHKeysign yes
	ServerAliveCountMax 3
	ServerAliveInterval 60

If the home directory of the client host is one that is shared with other machines, say using NFS or AFS, then it may be useful to look at the NoHostAuthenticationForLocalhost directive, too.

As a bit of trivia, the program ssh-keysign(8) itself must be SUID root. But SUID was probably set when it was installed and no changes there are needed.

3. Set Up the Client System's Own Host Keys

edit

The program ssh-keysign(8) needs to read the client system's private host keys in /etc/ssh/. Installing the OpenSSH server will create these keys but if the OpenSSH server has not been installed on the client system, it does not need to be. It is enough to have the keys by themselves. If the client system's private host keys do not exist, it will be necessary to add them manually using ssh-keygen(1) before host-based authentication will work.

$ ssh-keygen -A

The default path is /etc/ssh/ when using the -A option.

Note that although the client's system does not have to have an SSH server actually running in order to use host-based authentication to reach another system, it is entirely feasible to install but then disable or uninstall the SSH server on the client's server as a way to get the host keys in place.

Server-side Configurations for Host-based Authentication

edit

Three files on the server or target host must be modified to enable and allow host-based authentication:

/etc/shosts.equiv same syntax as old rhosts.equiv
/etc/ssh/ssh_known_hosts holds the host public keys from the clients
/etc/ssh/sshd_config turns on host-based authentication

The exact location of shosts.equiv may vary depending on operating system and distro.

1. Registering the Allowed Client Systems with the Server

edit

The shosts.equiv file must contain a list of the client systems which are allowed to attempt host-based authentication. The file can contain host names, IP addresses, or net groups. It is best to keep this file simple and oriented to just the list of hosts, either by name or IP number. It provides only the first cut, anyway. For fine tuning, use sshd_config(5) instead to set or revoke access for specific users and groups.

It is important to note that if you are using the shosts.equiv file, the user name on the client and server system must match. For instance, to connect to bob@server1.example.org, the user name on the client must also be bob. If you need user 'alice' to connect to bob@server1.example.org, you need to specify this in the .shosts file for 'bob', not in the global shosts.equiv file.

client1.example.org
192.0.2.102
client8.example.org -bull
@statcluster

This file resides in the directory /etc/ in OpenBSD, which this book uses for the reference system when possible. On other systems the file might be found in the directory /etc/ssh/. Either way, the shosts.equiv file identifies which addresses are allowed to try authenticating. Check the manual page for sshd(1) on the actual system being used to be sure of the correct location.

Hunt for a manual page hosts.equiv(5) for more details on .shosts.equiv (and .shosts.), but consider that its use is long since deprecated and most systems won't even have that manual page.

Leftovers from the Old Days

edit

Two legacy files may be on really old systems. and are optionally modified if they are referred to from within shosts.equiv. Each line in the netgroup file consists of a net group name followed by a list of the members of the net group, specifically host, user, and domain.

/etc/netgroup - default netgroup list
/etc/netgroup.db - netgroup database, build from netgroup

However, these are mostly legacy from the old rhosts and should be avoided.

Another leftover is using .shosts in each account's home directory. That would be a local equivalent to .shosts.equiv. In that way, individual users can have a local .shosts containing a list of trusted remote machines, or user-machine pairs, which are allowed to try host-based authentication.

.shosts must not writable by any group or any other users. Permissions set to 0644 should do it. The usage and format of .shosts is exactly the same as .rhosts, but allows host-based authentication without permitting login by insecure, legacy tools rlogin and rsh. The list is one line per host. The first column is obligatory and contains the name or address of the host permitted to attempt host-based authentication.

However a global .shosts.equiv is preferable to having .shosts in each and every home directory.

2. Populate the Server with the Client's Public Keys

edit

The client systems listed in the server's shosts.equiv must also have their public keys in /etc/ssh/ssh_known_hosts on the server in order to be acknowledged. There are three required data fields per line. First is the host name or IP address or comma separated list of them, corresponding to those from shosts.equiv. Next is the key type, either ssh-rsa for RSA keys or ssh-ed25519 for Ed25519 keys or ecdsa-sha2-nistp256 for ECDSA keys. The third field is the public key itself. Last, and optionally, can be a comment about the key.

desktop,192.0.2.102 ssh-rsa AAAAB3NzaC1yc2EAAAABIw ... qqU24CcgzmM=

Just like step one for the client, there are many ways of collecting the public key information from the client and getting it to the server. It can be copied using sftp(1), copied from ~/.ssh/known_hosts, or grabbed using ssh-keyscan(1). Though the latter two methods only work if the client system also has an SSH server running.

$ ssh-keyscan -t rsa client.example.org | tee -a /etc/ssh/ssh_known_hosts

3. System-wide Server Configuration

edit

The third file that must be changed on the server is sshd_config(5). It must be told to allow host-based authentication by setting the HostbasedAuthentication directive, either for all users or just some users or just certain groups.

HostbasedAuthentication yes

Host-based authentication can be limited to specific users or groups. Here is an example excerpt from sshd_config(5) allowing allow any user in the group 'cluster2' to let the hosts authenticate on their behalf:

Match Group cluster2
        HostbasedAuthentication yes

Certain host keys types can be allowed using HostbasedAcceptedAlgorithms, formerly HostbasedAcceptedKeyTypes, with a comma-delimited list of acceptable key algorthms. All the key algorithms which are to be allowed must be listed because those not listed are not allowed. Patterns can be used in the whitelist. Below, Ed25519 and ECDSA keys are allowed, but others, such as RSA and DSA are not.

HostbasedAuthentication yes
HostbasedAcceptedAlgorithms ssh-ed25519*,ecdsa-sha2*

Host Names and Other DNS Matters

edit

Make complete DNS entries for the clients if possible, including allowing reverse lookups. If the client machine is not listed in DNS, then the server might have trouble recognizing it. In that case you might have to tell sshd(8) not to do reverse lookups in DNS for connecting hosts. This can be a problem on informal LANs where hosts have addresses but no registered host names. Here is an example from /etc/ssh/sshd_config to work around lack of DNS records for the client using the HostbasedUsesNameFromPacketOnly directive.

HostbasedAuthentication yes
HostbasedUsesNameFromPacketOnly yes

Don't add this directive unless the normal way fails. Otherwise it can interfere and prevent authentication.

Sometimes the host trying to connect gets identified to the target host as something other than what was expected. That too will block authentication. So make sure that the configuration files match what the host actually calls itself.

Debugging

edit

Configuration should be quite straight forward, with small changes in only three files on the server and two on the client to manage. If there are difficulties, be prepared to run sshd(8) standalone at debug level 1 (-d) to 3 (-ddd) and ssh(1) at debug level 3 (-vvv) a few times to see what you missed. The mistakes have to be cleared up in the right order, so take it one step at a time.

If the server produces the message "debug3: auth_rhosts2_raw: no hosts access file exists" turns up, the shosts.equiv file is probably in the wrong place or missing and no fallback ~/.shosts lies in reserve in that account.

If the server cannot find the key for the client despite it being in known_hosts and if the client's host name is not in regular DNS, then it might be necessary to add the directive HostbasedUsesNameFromPacketOnly. This uses the name supplied by the client itself rather than doing a DNS lookup.

Here is a sample excerpt from a successful host-based authentication for user 'fred' from the host at 192.0.2.102, also known as desktop1, using an Ed25519 key. The server first tries looking for an ECDSA key and does not find it.

# /usr/sbin/sshd -ddd
debug2: load_server_config: filename /etc/ssh/sshd_config
...
debug3: /etc/ssh/sshd_config:111 setting HostbasedAuthentication yes
debug3: /etc/ssh/sshd_config:112 setting HostbasedUsesNameFromPacketOnly yes
...
debug1: sshd version OpenSSH_6.8, LibreSSL 2.1
...
debug1: userauth-request for user fred service ssh-connection method hostbased [preauth]
debug1: attempt 1 failures 0 [preauth]
debug2: input_userauth_request: try method hostbased [preauth]
debug1: userauth_hostbased: cuser fred chost desktop1. pkalg ecdsa-sha2-nistp256 slen 100 [preauth]
...
debug3: mm_answer_keyallowed: key_from_blob: 0x76eede00
debug2: hostbased_key_allowed: chost desktop1. resolvedname 192.0.2.102 ipaddr 192.0.2.102
debug2: stripping trailing dot from chost desktop1.
debug2: auth_rhosts2: clientuser fred hostname desktop1 ipaddr desktop1
debug1: temporarily_use_uid: 1000/1000 (e=0/0)
debug1: restore_uid: 0/0
debug1: fd 4 clearing O_NONBLOCK
debug2: hostbased_key_allowed: access allowed by auth_rhosts2
debug3: hostkeys_foreach: reading file "/etc/ssh/ssh_known_hosts"
debug3: record_hostkey: found key type ED25519 in file /etc/ssh/ssh_known_hosts:1
debug3: load_hostkeys: loaded 1 keys from desktop1
debug1: temporarily_use_uid: 1000/1000 (e=0/0)
debug3: hostkeys_foreach: reading file "/home/fred/.ssh/known_hosts"
debug1: restore_uid: 0/0
debug1: check_key_in_hostfiles: key for host desktop1 not found
Failed hostbased for fred from 192.0.2.102 port 10827 ssh2: ECDSA SHA256:CEXGTmrVgeY1qEiwFe2Yy3XqrWdjm98jKmX0LK5mlQg, client user "fred", client host "desktop1"
debug3: mm_answer_keyallowed: key 0x76eede00 is not allowed
debug3: mm_request_send entering: type 23
debug2: userauth_hostbased: authenticated 0 [preauth]
debug3: userauth_finish: failure partial=0 next methods="publickey,password,keyboard-interactive,hostbased" [preauth]
debug1: userauth-request for user fred service ssh-connection method hostbased [preauth]
debug1: attempt 2 failures 1 [preauth]
debug2: input_userauth_request: try method hostbased [preauth]
debug1: userauth_hostbased: cuser fred chost desktop1. pkalg ssh-ed25519 slen 83 [preauth]
debug3: mm_key_allowed entering [preauth]
debug3: mm_request_send entering: type 22 [preauth]
debug3: mm_key_allowed: waiting for MONITOR_ANS_KEYALLOWED [preauth]
debug3: mm_request_receive_expect entering: type 23 [preauth]
debug3: mm_request_receive entering [preauth]
debug3: mm_request_receive entering
debug3: monitor_read: checking request 22
debug3: mm_answer_keyallowed entering
debug3: mm_answer_keyallowed: key_from_blob: 0x7e499180
debug2: hostbased_key_allowed: chost desktop1. resolvedname 192.0.2.102 ipaddr 192.0.2.102
debug2: stripping trailing dot from chost desktop1.
debug2: auth_rhosts2: clientuser fred hostname desktop1 ipaddr desktop1
debug1: temporarily_use_uid: 1000/1000 (e=0/0)
debug1: restore_uid: 0/0
debug1: fd 4 clearing O_NONBLOCK
debug2: hostbased_key_allowed: access allowed by auth_rhosts2
debug3: hostkeys_foreach: reading file "/etc/ssh/ssh_known_hosts"
debug3: record_hostkey: found key type ED25519 in file /etc/ssh/ssh_known_hosts:1
debug3: load_hostkeys: loaded 1 keys from desktop1
debug1: temporarily_use_uid: 1000/1000 (e=0/0)
debug3: hostkeys_foreach: reading file "/home/fred/.ssh/known_hosts"
debug1: restore_uid: 0/0
debug1: check_key_in_hostfiles: key for desktop1 found at /etc/ssh/ssh_known_hosts:1
Accepted ED25519 public key SHA256:BDBRg/JZ36+PKYSQTJDsWNW9rAfmUQCgWcY7desk/+Q from fred@desktop1
debug3: mm_answer_keyallowed: key 0x7e499180 is allowed
debug3: mm_request_send entering: type 23
debug3: mm_key_verify entering [preauth]
debug3: mm_request_send entering: type 24 [preauth]
debug3: mm_key_verify: waiting for MONITOR_ANS_KEYVERIFY [preauth]
debug3: mm_request_receive_expect entering: type 25 [preauth]
debug3: mm_request_receive entering [preauth]
debug3: mm_request_receive entering
debug3: monitor_read: checking request 24
debug3: mm_answer_keyverify: key 0x7e49a700 signature verified
debug3: mm_request_send entering: type 25
Accepted hostbased for fred from 192.0.2.102 port 10827 ssh2: ED25519 SHA256:BDBRg/JZ36+PKYSQTJDsWNW9rAfmUQCgWcY7desk/+Q, client user "fred", client host "desktop1"
debug1: monitor_child_preauth: fred has been authenticated by privileged process
...

Note any warnings or error messages and read them carefully. If there is too much output, remember the -e option for the server and use it to save the debugging output to a separate file and then read that file afterwards.

Since this method relies on the client as well as the server, running the client with increased verbosity can sometimes help too.

$ ssh -v fred@server.example.org
$ ssh -vv fred@server.example.org
$ ssh -vvv fred@server.example.org

If there is too much output from the client to handle, remember the -E option and redirect the debug logs to a file and then read that file at leisure.