Password-free secure but universal ssh access for FreeBSD

Suppose you have a compiled binary /bin/prog that reads from standard in and writes to standard out, that is, a typical Unix filter program. You wish to allow anyone on the internet to process files through this program with a minimum of fuss at their end. In particular, they shouldn't need to do any programing, install any software or respond to any prompts. They will already have software of their own choosing that is scriptable and can shell out to the OS on their system.

In our case the program calculates tax liability from demographic and income information, but there are many potential uses, such file format conversions, statistical processing, etc. This type of service is usually provided via a webpage, but common browsers require substantial knowledge to script, and we want our tax calculator to be callable from within the users favorite statistical analysis program.

Notice that by secure, we mean in the sense that a public water fountain can be secure (against damage or misuse) without excluding any person from taking a drink. So we have no interest in using certificates to bypass passwords.

Choosing a protocol

We should pick a protocol that is scriptable, widely available, and routinely allowed through firewalls. "Available" means available by default in Windows, Linux and OSX as many potential users are prohibited by security policies from installing binary executables. The windows ftp client is scriptable, but institutional firewalls often block the only connection type provided by Windows ftp. None of the browsers are scriptable except by very knowledgable programmers.

We note that all Linux and OSX computers, and the Windows 10/11 OS since August 2nd, 2016 come with a functioning ssh client, (openssh) so we use that for the network connection.

We suppose that the filter program is prog and the userid for clients to use is proguser. However, if users are to embed the process in scripts sshd will need to avoid prompting the user for a password, certificat authorization or printing a gratuitous hello message. So we modify several configuration files on our server.

Preparing FreeBSD

Starting with a default FreeBSD installation, with sshd enabled, the following changes are required to allow password free, prompt free access with loginid proguser:
  1. Install the program at /bin/prog with execute permission for proguser (at least).
  2. Run the adduser program to add user proguser accepting all defaults except PermitEmptyPasswords should be yes and shell should be "proguser". Leave "Use password authentication as "yes". (The exact wording of the prompts varies).
  3. Examine /etc/master.passwd to confirm that the password hash for proguser is empty. I had to remove the asterisk from the password field. A correct record would like this:

    proguser::1001:1001::0:0:Example User:/home/proguser:/bin/sh

  4. Run pw usermod proguser -w none
  5. Run pwd_mkdb -p /etc/master.passwd

    Modify /etc/ssh/sshd_config with the following changes:

  6. Edit /etc/pam.d/sshd to add option nullok to the pam_unix.so line, giving:
    auth required pam_unix.so no_warn try_first_pass nullok
  7. Uncomment PermitEmptyPasswords and change no to yes
  8. Append at the end of the file
    Match proguser
    ForceCommand /bin/prog
  9. You may also wish to uncomment and change to "no"
    AllowAgentForwarding
    Allow X11 forwarding
    AllowTCPForwarding
    depending on your site security requirements.
  10. You might also want to add
    port 22
    port 80
    port 443
    to sshd_config so that users behind ultra-strict firewalls could use the traditional http ports, which are almost never restricted. Of course that means you can't run a webserver on the same machine.
  11. Remove /etc/motd or /var/run/motd (depending on your FreeBSD version) or create /home/proguser/.hushlogin to remove the login message of the day.
  12. Reboot.
At this point any remote user can submit file.foo with a one line command:

ssh proguser@example.com < file.foo

and after accepting a few prompts, file.foo will be passed to the server for processing. The prompts will cause problems for scripting, and can be avoided with a few ssh options:

ssh -T -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null proguser@example.com < file.foo

The first option disables host key checking where there is no entry for the host, and the second option disables it for the case where there is already an entry for the host. Those actions would otherwise cause user prompts. Note that there may be an ECDSA warning, but it goes to standard error so that it won't disturb standard out.

It is of course quite a security hole if prog allows user to start a shell, or has any buffer overflow bugs. In our case the program was a fortran program that reads lists of integers, and it does not call any external programs. We could be sure that no input could cause problems. sshd uses the user's default shell to execute any passed commands, so that is not a way to bypass prog.

A more flexible approach

The ChrootDirectory option of sshd_config offers an alternative approach that allows a selection of programs to be offered on a single login, and allows for program arguments to be passed. In this case a chroot directory would need to be established, and ChrootDirectory substituted for ForceCommand. Instructions for chroot are elsewhere on the Internet. Then the remote user could run:

ssh proguser@example.com < file.foo ./prog1 arg1 arg2

to select prog1 from among the programs available in the chroot directory, and pass it several arguments.

Rationale

We find that many users have very limited control of their computing environment and are not sophisticated with respect to modern computing patterns. In many cases it would take years to obtain permission to install a binary, and many users do not have access to compilers. Asking a non-computing professional to deal with certificates is a major obstacle to adoption and would not suppress all prompts. Of course, everyone can upload from a browser, but we wanted users to be able to embed the calculations in a script written by us, or by themselves. Netcat would solve many of our problems, but is not part of the default install in any widely used system. Using ssh meets nearly everyone's needs.

This has been tested on Freebsd 11.2,12.1,13.0 and 13.1. I don't see why essentially the same thing shouldn't work in Linux (subject to differences in the pam.d file names), but I haven't tried it. Nor have I tried using pam_exec(8). Comments and suggestions are welcome.

Widoch blogs knowledgably on this topic for Linux systems. See also this on FreeBSD .

Daniel Feenberg
feenberg@nber.org
27 April 2023