SSHD CONFIGURATION FOR FILE UPLOADS I want to test and debug a `sshd' configuration. My aim is to create some kind of configuration that restricts a user or key to one task: uploading files into a specific directory. This entry contains notes towards this objective. The first section sets out the steps for setting up an ad-hoc `sshd' process to test out arbitrary configurations. The second section covers my attempts and eventual success to meet my objective. Testing sshd configurations ---------------------------------------------------------------------- Invoke ad-hoc sshd configuration ...................................................................... I can test an arbitrary `sshd' configuration by invoking the program with a debug flag and a custom configuration file, specified as `-d' and `-f' respectively. The program must be invoked with elevated permissions. This is so it can read the system host keys (and maybe open a port?). `doas /usr/sbin/sshd -d -f new_sshd_config' It is necessary to invoke `sshd' with an absolute path. A StackOverflow post cites this part of the release notes for 3.9 as the reason: ,---- | Make sshd(8) re-execute itself on accepting a new connection. This security measure ensures that all execute-time randomisations are reapplied for each connection rather than once, for the master process' lifetime. This includes mmap and malloc mappings, shared library addressing, shared library mapping order, ProPolice and StackGhost cookies on systems that support such things `---- In otherwords, `sshd' needs to know exactly where it can re-execute itself. Temporarily allow new port through packet filter ...................................................................... I want to temporarily punch a hole in the firewall to permit connections on the port associated with my ad-hoc `sshd'. If my `pf.conf' is using lists only, I could add a new rule line to the file. If my `pf.conf' is using tables, I can simply add a new IP to the table from the command line. Connect with ssh client ...................................................................... Now from my client, I connect to the `sshd' process. Of course, I need to specify the port for this process using the `-p' flag. When login is successful, helpful debugging information is displayed. ,---- | Environment: | USER=roygbyte | LOGNAME=roygbyte | HOME=/home/roygbyte | PATH=/usr/bin:/bin:/usr/sbin:/sbin:/usr/X11R6/bin:/usr/local/bin:/usr/local/sbin | MAIL=/var/mail/roygbyte | SHELL=/bin/ksh | TERM=rxvt-256color | SSH_TTY=/dev/ttypi `---- Note that `-G' can be helpful for debugging client connections, too. This flag causes `ssh' to "print its configuration after evaluating Host and Match blocks and exiting." Small journey towards my objective ---------------------------------------------------------------------- First, a note about the journey below: the knowledge gained and applied was largely gathered from the book Absolute OpenBSD by Michael W. Lucas. Restrictions via Match block? ...................................................................... My initial assumption was that I could create and lock-down a user specifically for this task of uploading files. In effect, I could restrict the user with a `Match' block. The block would be declared to put the user inside a chroot, prohibit things like X11, and force a `rsync' command. I found this approach tedious. I needed to create the user and setup a home directory with all the `ssh' fixins. So I tried to take a shortcut, skipping the home directory setup and specifying the `AuthorizedKeysFile' option inside of the `Match' block to use a key contained within another user's home directory. This was a horrible idea, and I abandoned it when the web of permissions required to succeed overwhelmed me. I did however observe how and when `sshd' switches to the user it is authenticating during the authorized key lookup. Restrictions via key? ...................................................................... My next thought was that I could modify an existing user's `authorized_keys' file. Perhaps I could add a key specifically for my file transfer operation, and restrict it from other operations with key arguments. Lo and below, this was a better way. I began by testing key argument I saw in Absolute OpenBSD: ,---- | restrict,command="internal-sftp -d public_gopher" <ssh key> `---- I found I could transfer files using programs like `sftp' and `scp', but not `rsync', which I want to use for its great features. That's probably no surprise. According to the authors of Wikipedia, `rsync' transfers files by connecting to a `rsync' process on the remote machine: "Upon connection [through a secure shell], a command is issued to start an rsync process on the remote host, which uses the connection thus established." I needed to revise the `command' argument to use `rsync', but I am not sure what that command should be. So I do some practical testing and debugging to find the value. Here's how. - On the server, I create a new `sshd' process with debugging enabled. This process uses a unique port, which must be permitted through the firewall and specified in any client connection attempts. - On the client, I create a new key pair, `id_rsync_operation' for this operation. It does not use a password. I transfer the public key to the server. - On the server, I add the key that will be used explicitly for rsync to the user's `authorized_keys' file. Then, I restrict and force the `rsync' command: `restrict,command'"rsync" <ssh-key>=. The forced command is too simple right now, I figured, but having it listed will let me gather some new information from the next step. - On the client, I modify my `ssh' `config' file and add a new `Match' block for a `host' that will be used exclusively for this file transfer operation. I use a bogus host, `sftp.roygbyte.com'. Later, in my `rsync' command, I will use that same host in the command's destination argument. ,---- | Match host sftp.roygbyte.com | User roygbyte | Hostname roygbyte.com | Port <sshd process port> | IdentitiesOnly yes # Ignore potential ssh-key-agent keys. | IdentityFile ~/.ssh/id_rsync_operation `---- - On the local machine, attempt an `rsync' transfer: `rsync -rt public_gopher/* roygbyte@sftp.roygbyte.com:public_gopher'. The operation will fail. That's OK. Environment information will be printed to the terminal anyways. And therein lies the true command I am after. ,---- | > rsync -rt public_gopher/* roygbyte@sftp.roygbyte.com:public_gopher | Environment: | USER=roygbyte | LOGNAME=roygbyte | ... | SSH_ORIGINAL_COMMAND=rsync --server -r -t . public_gopher `---- - Copy the value of `SSH_ORIGINAL_COMMAND' and use that as the value for `command' within `authorized_keys' on the server. In effect: ,---- | restrict,command="rsync --server -r -t . public_gopher" ssh-ed25518 <...> `---- - Close the client connection and try the operation again. It should work. Now clean up the testing workflow and take a break. Postamble ---------------------------------------------------------------------- So it works! I can transfer files from my client to my server using a key that has been sufficiently restricted. I'm quite pleased that I succeeded in this task. I'm not sure if this writting will be helpful for anyone but myself... but I am sharing it anyways. It feels good to cast off my experiences into the gophersphere. Happy burrowing!