You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
provs/src/main/kotlin/io/provs/processors/RemoteUbuntuProcessor.kt

128 lines
3.9 KiB
Kotlin

package io.provs.processors
import io.provs.Secret
import io.provs.escapeAndEncloseByDoubleQuoteForShell
import io.provs.escapeNewline
import io.provs.platforms.SHELL
import net.schmizz.sshj.SSHClient
import net.schmizz.sshj.connection.channel.direct.Session
import net.schmizz.sshj.connection.channel.direct.Session.Command
import net.schmizz.sshj.transport.verification.PromiscuousVerifier
import org.slf4j.LoggerFactory
import java.io.BufferedReader
import java.io.File
import java.io.IOException
import java.io.InputStreamReader
import java.util.concurrent.TimeUnit
class RemoteProcessor(ip: String, user: String, password: Secret? = null) : Processor {
companion object {
@Suppress("JAVA_CLASS_ON_COMPANION")
private val log = LoggerFactory.getLogger(javaClass.enclosingClass)
}
private val ssh = SSHClient()
init {
try {
log.info("Connecting to $ip with user: $user with " + if (password != null) "password" else "ssh-key")
ssh.loadKnownHosts()
// todo: replace PromiscuousVerifier by more secure solution
ssh.addHostKeyVerifier(PromiscuousVerifier())
ssh.connect(ip)
if (password != null) {
ssh.authPassword(user, password.plain())
} else {
val base = System.getProperty("user.home") + File.separator + ".ssh" + File.separator
ssh.authPublickey(user, base + "id_rsa", base + "id_dsa", base + "id_ed25519", base + "id_ecdsa")
}
} catch (e: Exception) {
try {
ssh.disconnect()
} finally {
log.error("Got exception when initializing ssh: " + e.message)
throw RuntimeException("Error when initializing ssh", e)
}
}
}
override fun x(vararg args: String): ProcessResult {
return execute(true, *args)
}
override fun xNoLog(vararg args: String): ProcessResult {
return execute(false, *args)
}
private fun execute(logging: Boolean, vararg args: String): ProcessResult {
var prefix = "******************** Prov: "
if (logging) {
for (arg in args) {
prefix += " \"${arg.escapeNewline()}\""
}
} else {
prefix += "\"xxxxxxxx\""
}
log.info(prefix)
val cmdString: String =
if (args.size == 1)
args[0].escapeAndEncloseByDoubleQuoteForShell()
else
if (args.size == 3 && SHELL == args[0] && "-c" == args[1])
SHELL + " -c " + args[2].escapeAndEncloseByDoubleQuoteForShell()
else
args.joinToString(separator = " ")
var session: Session? = null
try {
session = ssh.startSession()
val cmd: Command = session!!.exec(cmdString)
val out = BufferedReader(InputStreamReader(cmd.inputStream)).use { it.readText() }
val err = BufferedReader(InputStreamReader(cmd.errorStream)).use { it.readText() }
cmd.join(100, TimeUnit.SECONDS)
val cmdRes = ProcessResult(cmd.exitStatus, out, err, args = args)
if (logging) {
log.info(cmdRes.toString())
}
session.close()
return cmdRes
} catch (e: Exception) {
try {
session?.close()
} finally {
// nothing to do
}
return ProcessResult(
-1,
err = "Error when opening or executing remote ssh session (Pls check host, user, password resp. ssh key) - ",
ex = e
)
}
}
override fun close() {
try {
log.info("Disconnecting ssh.")
ssh.disconnect()
} catch (e: IOException) {
// No prov required
}
}
protected fun finalize() {
close()
}
}