Search code examples
kotlindebuggingapache-sshd

How to debug org.apache.sshd.common.scp.ScpException: Received nack: Can not write to error?


I am writing a tool for testing the SSH connection with basic functionality: connect, upload a file, download a file.

My SftpTestServer implementation:

import org.apache.sshd.client.SshClient
import org.apache.sshd.client.scp.ScpClientCreator
import org.apache.sshd.client.session.ClientSession
import org.apache.sshd.common.file.virtualfs.VirtualFileSystemFactory
import org.apache.sshd.server.SshServer
import org.apache.sshd.server.auth.password.PasswordAuthenticator
import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider
import org.apache.sshd.server.scp.ScpCommandFactory
import org.apache.sshd.server.session.ServerSession
import org.apache.sshd.server.subsystem.SubsystemFactory
import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory
import java.io.File
import java.nio.file.Paths
import java.util.*


class SftpTestServer(
    private val host: String,
    private val port: Int,
    private val rootDir: File,
    private val userName: String,
    private val password: String
) : TestServer {
    private val server: SshServer
    private val client: SshClient

    private fun session(): ClientSession {
        val session = client.connect(userName, server.host, server.port).verify().session
        session.addPasswordIdentity(password)
        session.auth().verify().isSuccess
        return session
    }

    override fun start() {
        server.start()
        client.start()
    }

    override fun stop() {
        client.stop()
        server.stop()
    }

    private fun setupServer(host: String, port: Int, rootDir: File, userName: String, pwd: String): SshServer {
        val sshd = SshServer.setUpDefaultServer()
        sshd.keyPairProvider = SimpleGeneratorHostKeyProvider(File(rootDir, "sshd_key.ser").toPath())
        sshd.fileSystemFactory = VirtualFileSystemFactory(Paths.get(rootDir.absolutePath))
        sshd.port = port
        sshd.host = host
        sshd.passwordAuthenticator = PasswordAuthenticator { username: String, password: String, _: ServerSession? -> username == userName && password == pwd }
        sshd.commandFactory = ScpCommandFactory()
        val factoryList: MutableList<SubsystemFactory> = ArrayList()
        factoryList.add(SftpSubsystemFactory())
        sshd.subsystemFactories = factoryList
        return sshd
    }

    fun upload(file: File) {
        val creator = ScpClientCreator.instance()
        val scpClient = creator.createScpClient(session())
        scpClient.upload(Paths.get(file.absolutePath), rootDir.absolutePath)
    }

    fun listFiles(): List<String> {
        return listOf()
    }

    init {
        server = setupServer(host, port, rootDir, userName, password)
        client = setupClient()
    }

    private fun setupClient(): SshClient {
        return SshClient.setUpDefaultClient()
    }
}

My SftpTestServerTest implementation:

import org.apache.commons.io.FileUtils
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.*
import java.io.File

@Tag("servers")
class SftpTestServerTest {

    companion object {
        private const val HOST: String = "localhost"
        private const val PORT: Int = 23456
        private val TEST_FILE_RESOURCE: File = File("target/test-folder")
        private const val USERNAME: String = "username"
        private const val PASSWORD: String = "password"
    }

    private val server = SftpTestServer(HOST, PORT, TEST_FILE_RESOURCE, USERNAME, PASSWORD)

    @BeforeEach
    fun setUp() {
        server.start()

        FileUtils.deleteDirectory(TEST_FILE_RESOURCE)
        FileUtils.forceMkdir(TEST_FILE_RESOURCE)
    }

    @AfterEach
    fun tearDown() {
        server.stop()
    }

    @Test
    fun `should upload file from file path`() {
        val fileName = "src/test/resources/ssh_test_file.xml"
        val file = File(fileName)
        server.upload(file)
        assertThat(server.listFiles()).contains(fileName)
    }
}

Surprisingly, there is no much info on the web I could find about errors I get from org.apache.sshd.

org.apache.sshd.common.scp.ScpException: Received nack: Can not write to

How can I debug this error?

NOTE: I am using Mac. The issue shouldn't be OS-specific, though.


Solution

  • The issue was that I connected to the wrong path inside the SSH server with the SSH client.

    Therefore, my recommendations to debug this issue:

    • Check your permissions reading/writing to file system
    • Connect to the SSH server, check folder structure
    • Based on the previous steps, fix issues in SSH Client connection