SSH as a public service
──────────────────────────────────────────────────────────────────────
As the HTTP protocol becomes more and more ubiquitous for online
services,
I've looked into other protocols that could expose services online.

It turns out that ssh is a perfect candidate for that !
I've seen a couple examples in the wild, and there might be more:

       ssh [email protected]
       ssh [email protected]

So I decided to look into it myself, and set it up on my OpenBSD server.


0. Service program
------------------

First, you'll need a program to provide your service. We'll make a
program that gives the current day.

       # cat <<EOF > /usr/local/bin/today.sh
       #!/bin/sh
       date '+%A !'
       EOF
       # chmod +x /usr/local/bin/today.sh
       # /usr/local/bin/today.sh
       Tuesday !

You'll probably want something more useful, and we'll get to it later
on. For now, this will be enough.


1. Service account
------------------

It all starts with an account, that you'll use to connect to the
machine, and run the program. This account must not have a password,
and have restricted rights to the system resources.

So let's first create a login class for these users, to limit its
resource usage on the system:

       # cat <<EOF > /etc/login.conf.d/sshervice
       sshervice:\
               :path=/bin /usr/bin /usr/local/bin:\
               :umask=022:\
               :datasize=1024M:\
               :maxproc=32:\
               :openfiles=128:\
               :stacksize=1M:\
               :filesize=512M:
       EOF
       # cap_mkdb /etc/login.conf

See login.conf(5) for how to tweak these values to your likings.

       # groupadd _sshervice
       # adduser -d /var/sshervice \
               -L sshervice \
               -g _sshervice \
               -s /bin/sh \
               -p '' \
               today

At this point, you should be able to get a shell for your service
account without specifying a password (try as non-root user):

       $ su - today
       $ /usr/bin/whoami
       today
       $ pwd
       /var/sshervice


2. SSH access
-------------

You can login as this user, but it shouldn't be accessible from the
outside, because of how unsecure it is (at least that's the case on
OpenBSD).

Thanks to the "Match" keyword, OpenSSH can apply specific configuration
bits to a user or a group (see sshd_config(5) for details). So we'll
use that:

       # cat <<EOF > /etc/ssh/sshd_config_sshervice
       Match User today
               ForceCommand /usr/local/bin/today.sh

       Match Group _sshervice
               PasswordAuthentication yes
               PermitEmptyPasswords yes
               DisableForwarding yes
               ForceCommand /sbin/nologin
               MaxSessions 5
       EOF
       # echo 'Include sshd_config_sshervice' >>/etc/ssh/sshd_config
       # rcctl restart sshd


3. Final result
---------------

       $ ssh [email protected]
       Tuesday !


4. Going further
----------------

From there, it's all a matter of providing cool and/or useful services,
so you'll have to improve what's running as the "ForceCommand" of your
user.

Here are a few tips, in no particular order:

---

Read login.conf(5) and sshd_config(5). Really, do it.

---

Remember that you're giving strangers the ability to run programs on
your server. Take extra care to not provide them the ability to get
access to a shell. For example:

       #!/bin/sh
       date "$1"

This looks harmless, but passing it "-f/dev/passwd" would yield its
full content. So take extra care with the commands you allow !

---

Worth mentionning is that you can chroot(8) your service over ssh with
the following line under the "Match" block of sshd_config(5):

       ChrootDirectory /var/sshervice

This require to setup a proper chroot for your service to run the
program correctly.

---

Never trust user input.

---

use environment variable `$SSH_ORIGINAL_COMMAND` to allow users to
specify commands. Given the following script:

       #!/bin/sh
       date -- "+${SSH_ORIGINAL_COMMAND:-%A !}"

You can now specify the date format on the command line:

       $ ssh [email protected]
       Tuesday !
       $ ssh [email protected] %Y-%m-%d
       2022-08-23

---

Prefer self-contained programs over scripts.
Ideally statically link and chroot them.

---

If your program requires a TTY to run (like a pager for example), don't
forget to pass `-t` when you specify a command over ssh, because it does
not allocate a pseudo tty by default when passing arguments.

Imagine an online man page service:

       #!/bin/sh
       man "${SSH_ORIGINAL_COMMAND:-man}"

Make sure to add -t to force a pseudo TTY allocation for the pager to
work:

       $ ssh -t [email protected] sshd_config

20220823.1726