Before your server can support SFTP you must install an AbstractFileFactory implementation. AbstractFileFactory and AbstractFile are simple interfaces that allow you to implement your own file systems without the need to understand some of the complexities or nuances of the SFTP protocol.
There is no file system installed by default.
The SFTP file space is similar to *nix based file systems where file names use the slash character ‘/’ as a directory separator. The root path of the file system is ‘/’ and files starting with a slash are assumed to be absolute. Any files that do not start with a slash are relative to the user’s default directory. Typically the user’s default directory is also the users home directory, however, that is a matter of configuration. An empty path is also a valid path and it refers to the user’s default directory too.
We recommend that whatever file factory you ultimately choose to use, or implement, that you use this in conjunction with our VirtualFileFactory implementation. VirtualFileFactory provides an environment that allows you to mount different types of AbstractFileFactory implementations onto a single virtual file system. It does not impose its own file system, merely facilitates a virtual file system is also closely aligned with the requirements of the SFTP protocol and the SFTP file space described above.
We have already touched upon user default directories in our introduction and this warrants some further discussion before we attempt to configure SFTP. When you log in to a typical SFTP server, for example, an OpenSSH server hosted on a Linux machine, you will be logged into the users home directory on that machine. In the SFTP world, the place the user logs into is the user’s default directory. In the case of OpenSSH, the users home directory is being used as the user’s default directory.
Any path that is not absolute will be resolved from the user’s default directory. Our server provides the flexibility that the user’s default directory this can be any folder you want.
Our recommended configuration for SFTP is to mount one or more AbstractFile implementations onto our virtual file system. This file system comes with a very flexible AbstractFile implementation that uses Apache Commons VFS to enable to you map many different types of file systems as virtual mounts. For simplicity, we will only deal with local files but it should not be difficult for any developer to extend this to reference external file systems like FTP, SMB etc just by changing the URI used as the mount path.
So let’s configure our first file system using the most simple configuration available:
server.setFileFactory(new VirtualFileFactory(new VFSFileFactory()));
What does this do? This creates a virtual file system with a single mount for each user. The default mount maps the root directory of SFTP ‘/’ to the path “virtualfs/home/<username>” which is relative to the current working directory of the server process. If the directory does not exist, it will be created. If we connect to our server with this configuration we can see that as far as the user is concerned they see a chrooted file system with the ‘/’ path as their default directory.
sftp -oPort=2222 admin@localhost
Connected to admin@localhost.
sftp> pwd
Remote working directory: /
sftp>
To change the path used as the user’s default directory you can create your own mount template when creating the VirtualFileFactory:
server.setFileFactory(new VirtualFileFactory(
new VirtualMountTemplate("/", "tmp/${username}",
new VFSFileFactory())));
Here, again, the user’s default path as they see it on the SFTP server is ‘/’ but this time it’s mapped to “tmp/<username>”. Note that you can use the replacement token ${username} in any path or mount.
We can change the users mount path so their default directory appears to be on a different path:
server.setFileFactory(new VirtualFileFactory(
new VirtualMountTemplate("/home/${username}", "tmp/${username}",
new VFSFileFactory())));
Now when we connect via SFTP when performing pwd we see a different path
sftp> pwd
Remote working directory: /home/admin
If you are only going to provide a single mount to the user then we advise that you use the root path ‘/’. However, now that we have mounted the user’s default directory in a different location, we can add a further mount for the root where the user can access other files shared with them:
server.setFileFactory(new VirtualFileFactory(
new VirtualMountTemplate("/home/${username}", "tmp/${username}",
new VFSFileFactory()),
new VirtualMountTemplate("/", "tmp/shared",
new VFSFileFactory())));
Here we have added an additional mount so the user can access files they share with other users. Now when in the root directory they will see the home folder, as well as other folders in tmp/shared.
sftp> cd /
sftp> ls
report.txt home
We can, of course, take the opposite approach. Mapping the user’s default directory to the root path, and then mounting other shared mounts within it:
server.setFileFactory(new VirtualFileFactory(
new VirtualMountTemplate("/", "tmp/${username}",
new VFSFileFactory()),
new VirtualMountTemplate("/shared", "tmp/shared",
new VFSFileFactory())));
What the user sees now is a shared folder in the default directory.
It’s often the case that the file system should be read-only and users can only access files within it. This is made simple with yet another AbstractFile adapter type implementation, ReadOnlyFileFactoryAdapter. Simply wrap any AbstractFile implementation with this to provide a read-only file system.
For example, we can modify the previous example to make the shared folder read-only:
server.setFileFactory(new VirtualFileFactory(
new VirtualMountTemplate("/", "tmp/${username}",
new VFSFileFactory()),
new VirtualMountTemplate("/shared", "tmp/shared",
new ReadOnlyFileFactoryAdapter(new VFSFileFactory()))));