Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Apache Mina SFTP server side channel listener for incoming files

I am trying to figure out how to implement Server side listeners for a Java based SFTP server to alert me to an incoming file transfer. I'm using the latest version of Apache Mina. My scenario is for my server to simply receive a file from a client and do "something" to the file before storing it. That something could be error checking / rules validation / forwarding the contents elsewhere. The thing is I want to do that before it is saved on my system. I'm having difficulty with the documentation and can't find a working example that shows a listener implemented with access to the incoming file stream. I have a very simple server taken from a guide:

public void setupServer() throws IOException {

    sshd = SshServer.setUpDefaultServer();
    sshd.setFileSystemFactory(new NativeFileSystemFactory() {
        @Override
        public FileSystemView createFileSystemView(final Session session) {
            return new NativeFileSystemView(session.getUsername(), false) {
                @Override
                public String getVirtualUserDir() {
                    return testFolder.getRoot().getAbsolutePath();
                }
            };
        };
    });
    sshd.setPort(8001);
    sshd.setSubsystemFactories(Arrays
            .<NamedFactory<Command>> asList(new SftpSubsystem.Factory()));
    sshd.setCommandFactory(new ScpCommandFactory());
    sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider(testFolder
            .newFile("hostkey.ser").getAbsolutePath()));
    sshd.setPasswordAuthenticator(new PasswordAuthenticator() {
        public boolean authenticate(final String username, final String password,
                final ServerSession session) {

            return StringUtils.equals(username, USERNAME)
                    && StringUtils.equals(password, PASSWORD);
        }
    });

    // SessionListener event = new SessionListener();

    sshd.start();
}

That server is capable of receiving a file and storing it on the virtual file system. I can read the file / verify the contents but only after the file is received and stored. Basic authentication is fine for now, the authentication mechanisms are really well documented thankfully!

So my question is:

  • Is there a means to check dynamically when a connection is being made / when the contents are being transferred and to intercept that as it is happening i.e. before the file is actually committed to a directory.

or

  • Will I need to setup a listener to simply watch a directory for new files as they appear and process it accordingly?

Thanks in advance! Leigh.

like image 585
Leigh Griffin Avatar asked Apr 17 '15 11:04

Leigh Griffin


1 Answers

You seem to have an idea that the SFTP is similar protocol to an HTTP. I.e. the client opens a connection with "write" request (like HTTP PUT), sends a file contents of request body, disconnects and that's it.

That's not how the SFTP works.

The SFTP is like a remote file system. The client connects to the SSH/SFTP server and keeps the connection open. During the session, the client sends an "open" file request (with either read or write or both privileges) and gets a handle to the opened file. Then it sends a sequence of read/write-block requests using the file handle. And finally it closes the handle. During a single session, the client can (and typically does) read/write or both any number of files it likes. It can even have several files opened in parallel, accessing them in a completely random order. It is very similar to a way an application works with a local file system.

Implications:

  • You cannot reject connection when you do not like a file, because the connection request itself is not associated with a specific file. All you can is reject the file "open" (or "create") request.

    One way to intercept a file open/create request:

    • Derive NativeFileSystemView.
    • Derive NativeSshFile.
    • Override NativeFileSystemView.createNativeSshFile to create your NativeSshFile
    • Override NativeFileSystemView.isWritable()
  • You cannot redirect an SFTP connection. SSH/SFTP does not support connection "redirect" (unlike HTTP)

  • There's not a single moment where you have a complete file in a memory at once, so that you can inspect it somehow. Instead the client sends the file in chunks. You can of course reimplement the MINA SFTP "input stream" in a way that it keeps the file contents in a memory and inspect a complete contents once you get a "close" request; saving the file to a disk only once you are happy with the file. Beware of DOS attacks though.


A protocol that is more close to what you imagine is the SCP, which also runs over SSH. For it, there's ScpTransferEventListener.

like image 178
Martin Prikryl Avatar answered Sep 27 '22 23:09

Martin Prikryl