1. Introduction

With the increasing security consciousness of both users and administrators, older protocols like File Transfer Protocol (FTP) and File Transfer Protocol Secure (FTPS) get dropped in favor of more robust modern replacements such as SSH File Transfer Protocol (SFTP). Since it’s based on SSH, SFTP provides many features and less exposure.

In this tutorial, we explore SFTP and ways to check the storage space status on an SFTP server. First, we delve into the relationship between SSH and SFTP. After that, we get into the SSH subsystem mechanism. Next, we check how SFTP is configured in a typical SSH environment. Finally, we go over SFTP commands and extensions.

For brevity and security reasons, we only consider the newest iteration of SSH version 2 (SSHv2) implemented by OpenSSH.

We tested the code in this tutorial on Debian 12 (Bookworm) with GNU Bash 5.2.15. It should work in most POSIX-compliant environments unless otherwise specified.

2. SSH and SFTP

In normal conditions, SSH permits the execution of any command in the remote shell as long as we authenticate properly and have authorization:

$ ssh [email protected] hostname
[email protected]'s password:
xost

In this case, we run the hostname on 192.168.6.66 after successfully logging-in as user baeldung.

This isn’t the way FTP or SFTP work. They have a limited set of commands mainly targeting file transfer. Unlike FTP, SFTP isn’t actually an entirely separate protocol. In fact, it’s more or less a restricted SSH server.

3. The SSH Subsystem Mechanism

The SSH protocol can run so-called subsystems. An SSH subsystem is an abstraction layer on top of the SSH protocol.

For example, we might want to create a custom protocol for system identification. Let’s see how we might do that.

3.1. Create Executable

To begin with, we first write a simple executable script and place it in /usr/lib/openssh/:

$ cat /usr/lib/openssh/xid.sh
#!/usr/bin/env bash
hostname
$ chmod +x /usr/lib/openssh/xid.sh

Notably, the script only contains a shebang and a call to the hostname command.

Alternatively, we can use a restricted shell call or even a custom binary. In any case, this is what the SSH server calls upon session establishment.

3.2. Set Subsystem

To configure a new system, we use /etc/ssh/sshd_config.

Let’s set xid as a Subsystem:

$ cat /etc/ssh/sshd_config
[...]
# override default of no subsystems
Subsystem  xid  /usr/lib/openssh/xid.sh
[...]

Thus, the xid subsystem is in place, targeting the /usr/lib/openssh/xid.sh script.

3.3. Test Subsystem

To call a subsystem, we use the -s option of the ssh client:

$ ssh [email protected] -s xid
[email protected]'s password:
xost

Importantly, the -s switch should come after the server URI.

In this case, we pass xid as the desired subsystem. Consequently, we get the same result as before: the hostname of the system.

At this point, we understand SSH subsystems and their inner workings.

4. SFTP Configuration

Notably, SFTP servers also run as a subsystem of SSH. Further, the sftp client is equivalent to an ssh client call with the -s sftp subsystem:

$ ssh [...] -s sftp

On the server side, this is facilitated via a Subsystem line in sshd_config that defines the sftp system as the /usr/lib/openssh/sftp-server binary executable:

$ cat /etc/ssh/sshd_config
[...]
# override default of no subsystems
Subsystem  sftp  /usr/lib/openssh/sftp-server
[...]

In fact, both the sftp and scp client commands rely on this to function properly.

5. SFTP Commands

What constitutes a part of SFTP and what’s restricted relative to pure SSH depends on the SFTP server implementation. Some are embedded in sshd, while others are separate executables such as sftp-server.

In any case, as SFTP evolves, these implementations may need updates.

5.1. Basic Commands

SFTP supports a basic set of commands as returned by help at the sftp prompt:

sftp > help
Available commands:
bye                                Quit sftp
cd path                            Change remote directory to 'path'
chgrp [-h] grp path                Change group of file 'path' to 'grp'
chmod [-h] mode path               Change permissions of file 'path' to 'mode'
chown [-h] own path                Change owner of file 'path' to 'own'
...
symlink oldpath newpath            Symlink remote file
version                            Show SFTP version
!command                           Execute 'command' in local shell
!                                  Escape to local shell
?                                  Synonym for help

They mainly help with file transfer, data traversal, listing, and removal.

However, some commands require separate extensions to function.

5.2. Updates and Extensions

Although SFTP specifications already exist for versions 3, 4, 5, and 6 of the protocol, OpenSSH and other SSH servers usually only support SFTP version 3 (SFTPv3) by default.

Naturally, we can replace the executable related to our Subsystem to use any SFTP implementation, but the collaboration between it and SSH might be inconsistent.

On the other hand, SSH_FXP_EXTENDED requests, as defined by SFTPv3, can facilitate running any command or code.

5.3. Proprietary Commands

Since SFTP runs on SSH servers, we might be able to execute any command via ssh.

For example, one way to check the storage of an SFTP server is by using ssh directly with the df command:

$ ssh [email protected] df
[email protected]'s password:
Filesystem     1K-blocks    Used Available Use% Mounted on
udev              966610       0    966610   0% /dev
tmpfs             195660    5660    190000   4% /run
/dev/sda1       16660010 1004301  15655709   9% /
[...]

However, many SSH server configurations restrict any local sessions except for the SFTP subsystem. How a given system does this varies, but it generally involves non-login users, non-existent shells, and sshd_config modification.

Still, we might want to check the available storage as part of our SFTP usage.

5.4. Storage Checks

Although often useful for the general operation of an (S)FTP server, storage checks aren’t implemented in the first versions of SFTP.

So, **OpenSSH includes the statvfs implementation as a custom extension for storage checks called [email protected]**. As expected, its implementation comes in the form of an OpenSSH SFTP-specific df command:

sftp > df -h
    Size     Used    Avail   (root)    %Capacity
  16.6GB    1.1GB   15.5GB   11.5GB          91%

Still, SFTPv6 provides a storage status interface out of the box without the need for a custom statvfs implementation. This means that a few applications can directly leverage the functionality:

So, using any SFTPv6-enabled server and client combination should be sufficient for storage checks via the relevant feature.

6. Summary

In this article, we talked about the SSH File Transfer Protocol (SFTP), how it works with regard to SSH, and ways it can be extended.

In conclusion, although not all SFTP features are implemented in major SSH servers, we can use extensions for actions such as storage checks and similar.