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.
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: