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 kiosk@bitreich.org ssh play@ascii.town 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 < /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 < /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 < /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 today@cooldomain.tld 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 today@cooldomain.tld Tuesday ! $ ssh today@cooldomain.tld %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 man@cooldomain.tld sshd_config 20220823.1726