mirror of
https://github.com/TermoraDev/termora.git
synced 2026-01-16 02:12:58 +08:00
feat: known_hosts (#206)
This commit is contained in:
@@ -6,8 +6,6 @@ import com.formdev.flatlaf.util.SystemInfo
|
||||
import com.jetbrains.JBR
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.swing.Swing
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import org.jdesktop.swingx.JXLabel
|
||||
import java.awt.BorderLayout
|
||||
import java.awt.Component
|
||||
import java.awt.Desktop
|
||||
@@ -57,6 +55,7 @@ object OptionPane {
|
||||
pane.selectInitialValue()
|
||||
}
|
||||
})
|
||||
dialog.setLocationRelativeTo(parentComponent)
|
||||
dialog.isVisible = true
|
||||
dialog.dispose()
|
||||
val selectedValue = pane.value
|
||||
|
||||
@@ -29,7 +29,7 @@ import org.apache.sshd.common.session.SessionListener.Event
|
||||
import org.apache.sshd.common.util.net.SshdSocketAddress
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.util.EventObject
|
||||
import java.util.*
|
||||
import javax.swing.JComponent
|
||||
import javax.swing.SwingUtilities
|
||||
|
||||
@@ -87,9 +87,11 @@ class SSHTerminalTab(windowScope: WindowScope, host: Host) :
|
||||
terminal.write("SSH client is opening...\r\n")
|
||||
}
|
||||
|
||||
val owner = SwingUtilities.getWindowAncestor(terminalPanel)
|
||||
val client = SshClients.openClient(host).also { sshClient = it }
|
||||
client.serverKeyVerifier = DialogServerKeyVerifier(owner)
|
||||
// keyboard interactive
|
||||
client.userInteraction = TerminalUserInteraction(SwingUtilities.getWindowAncestor(terminalPanel))
|
||||
client.userInteraction = TerminalUserInteraction(owner)
|
||||
|
||||
val sessionListener = MySessionListener()
|
||||
val channelListener = MyChannelListener()
|
||||
|
||||
@@ -2,14 +2,20 @@ package app.termora
|
||||
|
||||
import app.termora.keymgr.OhKeyPairKeyPairProvider
|
||||
import app.termora.terminal.TerminalSize
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import org.apache.sshd.client.ClientBuilder
|
||||
import org.apache.sshd.client.SshClient
|
||||
import org.apache.sshd.client.channel.ChannelShell
|
||||
import org.apache.sshd.client.config.hosts.HostConfigEntryResolver
|
||||
import org.apache.sshd.client.config.hosts.KnownHostEntry
|
||||
import org.apache.sshd.client.kex.DHGClient
|
||||
import org.apache.sshd.client.keyverifier.KnownHostsServerKeyVerifier
|
||||
import org.apache.sshd.client.keyverifier.ModifiedServerKeyAcceptor
|
||||
import org.apache.sshd.client.keyverifier.ServerKeyVerifier
|
||||
import org.apache.sshd.client.session.ClientSession
|
||||
import org.apache.sshd.common.SshException
|
||||
import org.apache.sshd.common.channel.PtyChannelConfiguration
|
||||
import org.apache.sshd.common.config.keys.KeyUtils
|
||||
import org.apache.sshd.common.global.KeepAliveHandler
|
||||
import org.apache.sshd.common.kex.BuiltinDHFactories
|
||||
import org.apache.sshd.common.keyprovider.KeyIdentityProvider
|
||||
@@ -22,9 +28,16 @@ import org.eclipse.jgit.transport.CredentialsProvider
|
||||
import org.eclipse.jgit.transport.sshd.IdentityPasswordProvider
|
||||
import org.eclipse.jgit.transport.sshd.ProxyData
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.awt.Window
|
||||
import java.net.InetSocketAddress
|
||||
import java.net.Proxy
|
||||
import java.net.SocketAddress
|
||||
import java.nio.file.Paths
|
||||
import java.security.PublicKey
|
||||
import java.time.Duration
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import javax.swing.JOptionPane
|
||||
import javax.swing.SwingUtilities
|
||||
import kotlin.math.max
|
||||
|
||||
object SshClients {
|
||||
@@ -192,3 +205,73 @@ object SshClients {
|
||||
return sshClient
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class MyDialogServerKeyVerifier(private val owner: Window) : ServerKeyVerifier, ModifiedServerKeyAcceptor {
|
||||
override fun verifyServerKey(
|
||||
clientSession: ClientSession,
|
||||
remoteAddress: SocketAddress,
|
||||
serverKey: PublicKey
|
||||
): Boolean {
|
||||
val result = AtomicBoolean(false)
|
||||
|
||||
SwingUtilities.invokeAndWait {
|
||||
result.set(
|
||||
OptionPane.showConfirmDialog(
|
||||
parentComponent = owner,
|
||||
message = I18n.getString(
|
||||
"termora.host.verify-server-key",
|
||||
remoteAddress.toString().replace("/", StringUtils.EMPTY),
|
||||
KeyUtils.getKeyType(serverKey),
|
||||
KeyUtils.getFingerPrint(serverKey)
|
||||
),
|
||||
optionType = JOptionPane.OK_CANCEL_OPTION,
|
||||
messageType = JOptionPane.WARNING_MESSAGE,
|
||||
) == JOptionPane.OK_OPTION
|
||||
)
|
||||
}
|
||||
|
||||
return result.get()
|
||||
}
|
||||
|
||||
override fun acceptModifiedServerKey(
|
||||
clientSession: ClientSession?,
|
||||
remoteAddress: SocketAddress?,
|
||||
entry: KnownHostEntry?,
|
||||
expected: PublicKey?,
|
||||
actual: PublicKey?
|
||||
): Boolean {
|
||||
val result = AtomicBoolean(false)
|
||||
|
||||
SwingUtilities.invokeAndWait {
|
||||
result.set(
|
||||
OptionPane.showConfirmDialog(
|
||||
parentComponent = owner,
|
||||
message = I18n.getString(
|
||||
"termora.host.modified-server-key",
|
||||
remoteAddress.toString().replace("/", StringUtils.EMPTY),
|
||||
KeyUtils.getKeyType(expected),
|
||||
KeyUtils.getFingerPrint(expected),
|
||||
KeyUtils.getKeyType(actual),
|
||||
KeyUtils.getFingerPrint(actual),
|
||||
),
|
||||
optionType = JOptionPane.OK_CANCEL_OPTION,
|
||||
messageType = JOptionPane.WARNING_MESSAGE,
|
||||
) == JOptionPane.OK_OPTION
|
||||
)
|
||||
}
|
||||
|
||||
return result.get()
|
||||
}
|
||||
}
|
||||
|
||||
class DialogServerKeyVerifier(
|
||||
owner: Window,
|
||||
) : KnownHostsServerKeyVerifier(
|
||||
MyDialogServerKeyVerifier(owner),
|
||||
Paths.get(Application.getBaseDataDir().absolutePath, "known_hosts")
|
||||
) {
|
||||
init {
|
||||
modifiedServerKeyAcceptor = delegateVerifier as ModifiedServerKeyAcceptor
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,8 +115,9 @@ class SftpFileSystemPanel(
|
||||
try {
|
||||
val client = SshClients.openClient(host).apply { client = this }
|
||||
withContext(Dispatchers.Swing) {
|
||||
client.userInteraction =
|
||||
TerminalUserInteraction(SwingUtilities.getWindowAncestor(this@SftpFileSystemPanel))
|
||||
val owner = SwingUtilities.getWindowAncestor(this@SftpFileSystemPanel)
|
||||
client.userInteraction = TerminalUserInteraction(owner)
|
||||
client.serverKeyVerifier = DialogServerKeyVerifier(owner)
|
||||
}
|
||||
val session = SshClients.openSession(host, client).apply { session = this }
|
||||
fileSystem = SftpClientFactory.instance().createSftpFileSystem(session)
|
||||
|
||||
@@ -38,6 +38,9 @@ termora.doorman.mnemonic.title=Enter 12 mnemonic words
|
||||
termora.doorman.mnemonic.incorrect=Incorrect mnemonic
|
||||
|
||||
|
||||
# Hosts
|
||||
termora.host.verify-server-key=Host [{0}] key has been changed!<br/><br/>{1} key fingerprint is {2}<br/><br/>Are you sure you want to continue connecting?
|
||||
termora.host.modified-server-key=HOST [{0}] IDENTIFICATION HAS CHANGED!<br/><br/>Expected: {1} key fingerprint is {2}<br/><br/>Actual: {3} key fingerprint is {4}<br/><br/>Are you sure you want to continue connecting?
|
||||
|
||||
|
||||
# Settings
|
||||
|
||||
@@ -36,6 +36,11 @@ termora.doorman.mnemonic.title=输入 12 个助记词
|
||||
termora.doorman.mnemonic.incorrect=助记词错误
|
||||
|
||||
|
||||
# Hosts
|
||||
termora.host.verify-server-key=主机 [{0}] 密钥已经改变!<br/><br/>{1} 的指纹 {2}<br/><br/>你确定要继续连接吗?
|
||||
termora.host.modified-server-key=主机 [{0}] 身份已发生变化!<br/><br/>期待: {1} 的指纹 {2}<br/><br/>实际: {3} 的指纹 {4}<br/><br/>你确定要继续连接吗?
|
||||
|
||||
|
||||
termora.setting=设置
|
||||
termora.settings.restart.title=重启
|
||||
termora.settings.restart.message=设置修改将在重启后生效
|
||||
|
||||
@@ -35,6 +35,13 @@ termora.doorman.mnemonic-data-corrupted=無法從助記詞解密數據,資料
|
||||
termora.doorman.mnemonic.title=輸入 12 個助記詞
|
||||
termora.doorman.mnemonic.incorrect=助記詞錯誤
|
||||
|
||||
|
||||
|
||||
# Hosts
|
||||
termora.host.verify-server-key=主機 [{0}] 金鑰已經改變!<br/><br/>{1} 的指紋 {2}<br/><br/>你確定要繼續連線嗎?
|
||||
termora.host.modified-server-key=主機 [{0}] 身分已變更!<br/><br/>期待: {1} 的指紋 {2}<br/><br/>實際: {3} 的指紋 {4}<br/><br/>你確定要繼續連線嗎?
|
||||
|
||||
|
||||
termora.setting=設定
|
||||
termora.settings.restart.title=重啟
|
||||
termora.settings.restart.message=設定修改將在重新啟動後生效
|
||||
|
||||
Reference in New Issue
Block a user