feat: ssh ForwardAgent

This commit is contained in:
hstyi
2025-08-08 17:45:31 +08:00
committed by hstyi
parent 9646a98f6d
commit c67d5b0276
3 changed files with 42 additions and 9 deletions

View File

@@ -114,7 +114,8 @@ internal class SSHHostOptionsPane(private val accountOwner: AccountOwner) : Opti
?: AltKeyModifier.EightBit.name), ?: AltKeyModifier.EightBit.name),
"keywordHighlightSetId" to ((terminalOption.highlightSetComboBox.selectedItem as? KeywordHighlight)?.id "keywordHighlightSetId" to ((terminalOption.highlightSetComboBox.selectedItem as? KeywordHighlight)?.id
?: "-1"), ?: "-1"),
"timeout" to (terminalOption.timeoutTextField.value ?: 60).toString() "timeout" to (terminalOption.timeoutTextField.value ?: 60).toString(),
"forwardAgent" to tunnelingOption.forwardAgentCheckBox.isSelected.toString(),
) )
) )
@@ -182,6 +183,7 @@ internal class SSHHostOptionsPane(private val accountOwner: AccountOwner) : Opti
tunnelingOption.tunnelings.addAll(host.tunnelings) tunnelingOption.tunnelings.addAll(host.tunnelings)
tunnelingOption.x11ForwardingCheckBox.isSelected = host.options.enableX11Forwarding tunnelingOption.x11ForwardingCheckBox.isSelected = host.options.enableX11Forwarding
tunnelingOption.x11ServerTextField.text = StringUtils.defaultIfBlank(host.options.x11Forwarding, "localhost:0") tunnelingOption.x11ServerTextField.text = StringUtils.defaultIfBlank(host.options.x11Forwarding, "localhost:0")
tunnelingOption.forwardAgentCheckBox.isSelected = host.options.extras["forwardAgent"]?.toBoolean() ?: false
if (host.options.jumpHosts.isNotEmpty()) { if (host.options.jumpHosts.isNotEmpty()) {
val hosts = HostManager.getInstance().hosts().associateBy { it.id } val hosts = HostManager.getInstance().hosts().associateBy { it.id }
@@ -570,9 +572,10 @@ internal class SSHHostOptionsPane(private val accountOwner: AccountOwner) : Opti
} }
} }
protected inner class TunnelingOption : JPanel(BorderLayout()), Option { private inner class TunnelingOption : JPanel(BorderLayout()), Option {
val tunnelings = mutableListOf<Tunneling>() val tunnelings = mutableListOf<Tunneling>()
val x11ForwardingCheckBox = JCheckBox("X DISPLAY:") val x11ForwardingCheckBox = JCheckBox("X DISPLAY:")
val forwardAgentCheckBox = JCheckBox("Enable ForwardAgent")
val x11ServerTextField = OutlineTextField(255) val x11ServerTextField = OutlineTextField(255)
private val model = object : DefaultTableModel() { private val model = object : DefaultTableModel() {
@@ -649,6 +652,7 @@ internal class SSHHostOptionsPane(private val accountOwner: AccountOwner) : Opti
box.add(deleteBtn) box.add(deleteBtn)
x11ForwardingCheckBox.isFocusable = false x11ForwardingCheckBox.isFocusable = false
forwardAgentCheckBox.isFocusable = false
if (x11ServerTextField.text.isBlank()) { if (x11ServerTextField.text.isBlank()) {
x11ServerTextField.text = "localhost:0" x11ServerTextField.text = "localhost:0"
@@ -662,6 +666,13 @@ internal class SSHHostOptionsPane(private val accountOwner: AccountOwner) : Opti
x11Forwarding.add(x11ForwardingCheckBox) x11Forwarding.add(x11ForwardingCheckBox)
x11Forwarding.add(x11ServerTextField) x11Forwarding.add(x11ServerTextField)
val forwardAgent = Box.createHorizontalBox()
forwardAgent.border = BorderFactory.createCompoundBorder(
BorderFactory.createTitledBorder("ForwardAgent"),
BorderFactory.createEmptyBorder(4, 4, 4, 4)
)
forwardAgent.add(forwardAgentCheckBox)
x11ServerTextField.isEnabled = x11ForwardingCheckBox.isSelected x11ServerTextField.isEnabled = x11ForwardingCheckBox.isSelected
val panel = JPanel(BorderLayout()) val panel = JPanel(BorderLayout())
@@ -670,8 +681,13 @@ internal class SSHHostOptionsPane(private val accountOwner: AccountOwner) : Opti
panel.add(box, BorderLayout.SOUTH) panel.add(box, BorderLayout.SOUTH)
panel.border = BorderFactory.createEmptyBorder(0, 0, 8, 0) panel.border = BorderFactory.createEmptyBorder(0, 0, 8, 0)
val forwardingBox = Box.createHorizontalBox()
forwardingBox.add(x11Forwarding)
forwardingBox.add(Box.createHorizontalStrut(4))
forwardingBox.add(forwardAgent)
add(panel, BorderLayout.CENTER) add(panel, BorderLayout.CENTER)
add(x11Forwarding, BorderLayout.SOUTH) add(forwardingBox, BorderLayout.SOUTH)
} }

View File

@@ -0,0 +1,14 @@
package app.termora.plugin.internal.ssh
import org.apache.sshd.agent.local.ChannelAgentForwardingFactory
import org.apache.sshd.common.FactoryManager
import org.apache.sshd.common.channel.ChannelFactory
import org.eclipse.jgit.internal.transport.sshd.agent.JGitSshAgentFactory
import org.eclipse.jgit.transport.sshd.agent.ConnectorFactory
import java.io.File
internal class SshAgentFactory(factory: ConnectorFactory, homeDir: File?) : JGitSshAgentFactory(factory, homeDir) {
override fun getChannelForwardingFactories(manager: FactoryManager?): List<ChannelFactory> {
return listOf(ChannelAgentForwardingFactory.OPENSSH, ChannelAgentForwardingFactory.IETF)
}
}

View File

@@ -56,7 +56,6 @@ import org.apache.sshd.server.forward.AcceptAllForwardingFilter
import org.apache.sshd.server.forward.RejectAllForwardingFilter import org.apache.sshd.server.forward.RejectAllForwardingFilter
import org.eclipse.jgit.internal.transport.sshd.JGitClientSession import org.eclipse.jgit.internal.transport.sshd.JGitClientSession
import org.eclipse.jgit.internal.transport.sshd.JGitSshClient import org.eclipse.jgit.internal.transport.sshd.JGitSshClient
import org.eclipse.jgit.internal.transport.sshd.agent.JGitSshAgentFactory
import org.eclipse.jgit.internal.transport.sshd.agent.connector.PageantConnector import org.eclipse.jgit.internal.transport.sshd.agent.connector.PageantConnector
import org.eclipse.jgit.internal.transport.sshd.agent.connector.UnixDomainSocketConnector import org.eclipse.jgit.internal.transport.sshd.agent.connector.UnixDomainSocketConnector
import org.eclipse.jgit.internal.transport.sshd.proxy.AbstractClientProxyConnector import org.eclipse.jgit.internal.transport.sshd.proxy.AbstractClientProxyConnector
@@ -112,6 +111,8 @@ object SshClients {
env.putAll(host.options.envs()) env.putAll(host.options.envs())
val channel = session.createShellChannel(configuration, env) val channel = session.createShellChannel(configuration, env)
channel.isAgentForwarding = host.options.extras["forwardAgent"]?.toBoolean() == true
if (host.options.enableX11Forwarding) { if (host.options.enableX11Forwarding) {
if (channel is app.termora.x11.ChannelShell) { if (channel is app.termora.x11.ChannelShell) {
channel.xForwarding = true channel.xForwarding = true
@@ -386,7 +387,7 @@ object SshClients {
val channelFactories = mutableListOf<ChannelFactory>() val channelFactories = mutableListOf<ChannelFactory>()
channelFactories.addAll(ClientBuilder.DEFAULT_CHANNEL_FACTORIES) channelFactories.addAll(ClientBuilder.DEFAULT_CHANNEL_FACTORIES)
channelFactories.add(X11ChannelFactory.Companion.INSTANCE) channelFactories.add(X11ChannelFactory.INSTANCE)
builder.channelFactories(channelFactories) builder.channelFactories(channelFactories)
val sshClient = builder.build() as JGitSshClient val sshClient = builder.build() as JGitSshClient
@@ -395,12 +396,14 @@ object SshClients {
// JGit 会尝试读取本地的私钥或缓存的私钥 // JGit 会尝试读取本地的私钥或缓存的私钥
sshClient.keyIdentityProvider = KeyIdentityProvider { mutableListOf() } sshClient.keyIdentityProvider = KeyIdentityProvider { mutableListOf() }
// https://github.com/TermoraDev/termora/issues/1001
if (host.authentication.type == AuthenticationType.SSHAgent || host.options.extras["forwardAgent"]?.toBoolean() == true) {
// ssh-agent
sshClient.agentFactory = SshAgentFactory(ConnectorFactory.getDefault(), null)
}
// 设置优先级 // 设置优先级
if (host.authentication.type == AuthenticationType.PublicKey || host.authentication.type == AuthenticationType.SSHAgent) { if (host.authentication.type == AuthenticationType.PublicKey || host.authentication.type == AuthenticationType.SSHAgent) {
if (host.authentication.type == AuthenticationType.SSHAgent) {
// ssh-agent
sshClient.agentFactory = JGitSshAgentFactory(ConnectorFactory.getDefault(), null)
}
CoreModuleProperties.PREFERRED_AUTHS.set( CoreModuleProperties.PREFERRED_AUTHS.set(
sshClient, sshClient,
listOf( listOf(