Implement SSH port forwarding and improve jump host UI
Port Forwarding (new): - Add PortForward model with local/remote direction, bind/dest addresses - Implement ForwardManager in sshclient/portforward.go with full bidirectional tunnel support for both local and remote forwarding - Integrate ForwardManager into SSH client lifecycle (auto-start on connect, clean shutdown on close) - Add session manager methods: ListPortForwards, StartPortForward, StopPortForward with event emission - Add API surface: ListPortForwards, StartPortForward, StopPortForward - Add validation for port-forward rules in ValidateSSHConfig - Add port forwarding section to profile editor with direction, bind/dest host/port, label, and enable toggle - Add frontend event handling for forward:started/stopped/error - Style port-forward rows with charcoal card design matching jump hosts Jump Host UI improvements: - Add private key path field to jump host editor rows - Collect privateKeyPath in form value collection - Restyle jump host rows as two-line cards with clear field grouping
This commit is contained in:
@@ -15,7 +15,7 @@ import (
|
||||
|
||||
// Client manages a single SSH session lifecycle.
|
||||
// It handles connection establishment, authentication, PTY allocation,
|
||||
// terminal resize propagation, and clean shutdown.
|
||||
// terminal resize propagation, port forwarding, and clean shutdown.
|
||||
type Client struct {
|
||||
mu sync.Mutex
|
||||
config *models.Profile
|
||||
@@ -30,6 +30,7 @@ type Client struct {
|
||||
onClose func()
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
forwards *ForwardManager // port forwarding tunnels
|
||||
}
|
||||
|
||||
// NewClient creates a new SSH client bound to the given profile.
|
||||
@@ -193,6 +194,7 @@ func (c *Client) connectInternal(
|
||||
c.stdout = stdoutPipe
|
||||
c.stderr = stderrPipe
|
||||
c.connected = true
|
||||
c.forwards = NewForwardManager(sshConn)
|
||||
c.mu.Unlock()
|
||||
|
||||
go c.readLoop()
|
||||
@@ -242,11 +244,17 @@ func (c *Client) Close() error {
|
||||
c.connected = false
|
||||
session := c.session
|
||||
sshClient := c.sshClient
|
||||
fwdMgr := c.forwards
|
||||
onClose := c.onClose
|
||||
c.mu.Unlock()
|
||||
|
||||
c.cancel()
|
||||
|
||||
// Shut down any active port forwards first.
|
||||
if fwdMgr != nil {
|
||||
fwdMgr.StopAll()
|
||||
}
|
||||
|
||||
var firstErr error
|
||||
if session != nil {
|
||||
if err := session.Close(); err != nil && firstErr == nil {
|
||||
@@ -272,6 +280,13 @@ func (c *Client) IsConnected() bool {
|
||||
return c.connected
|
||||
}
|
||||
|
||||
// Forwards returns the ForwardManager for this client, or nil if not connected.
|
||||
func (c *Client) Forwards() *ForwardManager {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
return c.forwards
|
||||
}
|
||||
|
||||
// buildAuthMethods constructs the SSH authentication method list from the
|
||||
// profile's SSHConfig and the provided credential callbacks.
|
||||
func (c *Client) buildAuthMethods(sc models.SSHConfig, getPassword func() (string, error), getPassphrase func() (string, error)) ([]ssh.AuthMethod, error) {
|
||||
|
||||
Reference in New Issue
Block a user