update go ssh app
This commit is contained in:
@@ -2,8 +2,10 @@ package ssh
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
)
|
)
|
||||||
@@ -16,7 +18,7 @@ type SSHConfig struct {
|
|||||||
PrivateKey string
|
PrivateKey string
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConnectSSH handles both key-based and password authentication dynamically.
|
// ConnectSSH establishes an SSH connection and starts an interactive session.
|
||||||
func ConnectSSH(config SSHConfig) error {
|
func ConnectSSH(config SSHConfig) error {
|
||||||
log.Printf("Attempting SSH connection to %s@%s:%s", config.User, config.Host, config.Port)
|
log.Printf("Attempting SSH connection to %s@%s:%s", config.User, config.Host, config.Port)
|
||||||
|
|
||||||
@@ -25,8 +27,8 @@ func ConnectSSH(config SSHConfig) error {
|
|||||||
|
|
||||||
// Add key-based authentication if a private key is provided
|
// Add key-based authentication if a private key is provided
|
||||||
if config.PrivateKey != "" {
|
if config.PrivateKey != "" {
|
||||||
log.Println("Attempting key-based authentication...")
|
log.Println("Using private key authentication...")
|
||||||
key, err := ioutil.ReadFile(config.PrivateKey)
|
key, err := os.ReadFile(config.PrivateKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error reading private key: %v", err)
|
log.Printf("Error reading private key: %v", err)
|
||||||
return fmt.Errorf("failed to read private key: %w", err)
|
return fmt.Errorf("failed to read private key: %w", err)
|
||||||
@@ -43,7 +45,7 @@ func ConnectSSH(config SSHConfig) error {
|
|||||||
|
|
||||||
// Add password-based authentication if a password is provided
|
// Add password-based authentication if a password is provided
|
||||||
if config.Password != "" {
|
if config.Password != "" {
|
||||||
log.Println("Attempting password-based authentication...")
|
log.Println("Using password authentication...")
|
||||||
authMethods = append(authMethods, ssh.Password(config.Password))
|
authMethods = append(authMethods, ssh.Password(config.Password))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,13 +58,13 @@ func ConnectSSH(config SSHConfig) error {
|
|||||||
clientConfig := &ssh.ClientConfig{
|
clientConfig := &ssh.ClientConfig{
|
||||||
User: config.User,
|
User: config.User,
|
||||||
Auth: authMethods,
|
Auth: authMethods,
|
||||||
HostKeyCallback: ssh.InsecureIgnoreHostKey(), // Use a proper host key callback for production!
|
HostKeyCallback: ssh.InsecureIgnoreHostKey(), // For production, use a proper host key callback!
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dial the SSH server
|
|
||||||
address := fmt.Sprintf("%s:%s", config.Host, config.Port)
|
address := fmt.Sprintf("%s:%s", config.Host, config.Port)
|
||||||
log.Printf("Connecting to SSH server at %s...", address)
|
log.Printf("Connecting to SSH server at %s...", address)
|
||||||
|
|
||||||
|
// Connect to the SSH server
|
||||||
client, err := ssh.Dial("tcp", address, clientConfig)
|
client, err := ssh.Dial("tcp", address, clientConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to connect to SSH server: %v", err)
|
log.Printf("Failed to connect to SSH server: %v", err)
|
||||||
@@ -70,6 +72,50 @@ func ConnectSSH(config SSHConfig) error {
|
|||||||
}
|
}
|
||||||
defer client.Close()
|
defer client.Close()
|
||||||
|
|
||||||
log.Printf("Successfully connected to %s@%s:%s", config.User, config.Host, config.Port)
|
log.Println("SSH connection established. Starting interactive session...")
|
||||||
return nil
|
return startInteractiveSession(client)
|
||||||
|
}
|
||||||
|
|
||||||
|
// startInteractiveSession starts an interactive shell session.
|
||||||
|
func startInteractiveSession(client *ssh.Client) error {
|
||||||
|
// Create a new session
|
||||||
|
session, err := client.NewSession()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create SSH session: %w", err)
|
||||||
|
}
|
||||||
|
defer session.Close()
|
||||||
|
|
||||||
|
// Set up terminal modes
|
||||||
|
modes := ssh.TerminalModes{
|
||||||
|
ssh.ECHO: 1, // Enable echoing
|
||||||
|
ssh.TTY_OP_ISPEED: 14400,
|
||||||
|
ssh.TTY_OP_OSPEED: 14400,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request a pseudo-terminal
|
||||||
|
if err := session.RequestPty("xterm", 80, 40, modes); err != nil {
|
||||||
|
return fmt.Errorf("failed to request pseudo-terminal: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up input/output for the session
|
||||||
|
session.Stdin = os.Stdin
|
||||||
|
session.Stdout = os.Stdout
|
||||||
|
session.Stderr = os.Stderr
|
||||||
|
|
||||||
|
// Start an interactive shell
|
||||||
|
if err := session.Shell(); err != nil {
|
||||||
|
return fmt.Errorf("failed to start shell: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle Ctrl+C and other interrupts to cleanly close the session
|
||||||
|
signalChan := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(signalChan, os.Interrupt, syscall.SIGTERM)
|
||||||
|
go func() {
|
||||||
|
<-signalChan
|
||||||
|
session.Close()
|
||||||
|
os.Exit(0)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Wait for the session to end
|
||||||
|
return session.Wait()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +1,83 @@
|
|||||||
package tui
|
package tui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log"
|
||||||
|
"tui-ssh-app/ssh"
|
||||||
|
|
||||||
"github.com/rivo/tview"
|
"github.com/rivo/tview"
|
||||||
)
|
)
|
||||||
|
|
||||||
func StartTUI() {
|
func StartTUI() {
|
||||||
app := tview.NewApplication()
|
app := tview.NewApplication()
|
||||||
|
|
||||||
|
// Input fields for SSH configuration
|
||||||
|
userField := tview.NewInputField().SetLabel("User: ")
|
||||||
|
hostField := tview.NewInputField().SetLabel("Host: ")
|
||||||
|
portField := tview.NewInputField().SetLabel("Port: ").SetText("22")
|
||||||
|
passwordField := tview.NewInputField().SetLabel("Password: ")
|
||||||
|
privateKeyField := tview.NewInputField().SetLabel("Private Key Path: ")
|
||||||
|
|
||||||
|
// Output box for connection status
|
||||||
|
outputBox := tview.NewTextView().SetDynamicColors(true)
|
||||||
|
|
||||||
|
// Layout
|
||||||
form := tview.NewForm().
|
form := tview.NewForm().
|
||||||
AddInputField("User", "", 20, nil, nil).
|
AddFormItem(userField).
|
||||||
AddInputField("Host", "", 20, nil, nil).
|
AddFormItem(hostField).
|
||||||
AddInputField("Port", "22", 5, nil, nil).
|
AddFormItem(portField).
|
||||||
AddPasswordField("Password", "", 20, '*', nil).
|
AddFormItem(passwordField).
|
||||||
AddInputField("Private Key", "", 20, nil, nil).
|
AddFormItem(privateKeyField).
|
||||||
AddButton("Connect", func() {
|
AddButton("Connect", func() {
|
||||||
// Handle SSH connection logic
|
// Read inputs
|
||||||
|
user := userField.GetText()
|
||||||
|
host := hostField.GetText()
|
||||||
|
port := portField.GetText()
|
||||||
|
password := passwordField.GetText()
|
||||||
|
privateKey := privateKeyField.GetText()
|
||||||
|
|
||||||
|
// Validate inputs
|
||||||
|
if user == "" || host == "" || port == "" {
|
||||||
|
outputBox.SetText("[red]Error: All fields except Password/Private Key are required!").ScrollToEnd()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display connection status
|
||||||
|
outputBox.SetText("[yellow]Connecting...").ScrollToEnd()
|
||||||
|
|
||||||
|
// Perform SSH connection in a goroutine to avoid blocking the TUI
|
||||||
|
go func() {
|
||||||
|
err := ssh.ConnectSSH(ssh.SSHConfig{
|
||||||
|
User: user,
|
||||||
|
Host: host,
|
||||||
|
Port: port,
|
||||||
|
Password: password,
|
||||||
|
PrivateKey: privateKey,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("SSH connection failed: %v", err)
|
||||||
|
app.QueueUpdateDraw(func() {
|
||||||
|
outputBox.SetText("[red]Connection failed: " + err.Error()).ScrollToEnd()
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
app.QueueUpdateDraw(func() {
|
||||||
|
outputBox.SetText("[green]Connection successful!").ScrollToEnd()
|
||||||
|
})
|
||||||
|
}()
|
||||||
}).
|
}).
|
||||||
AddButton("Quit", func() {
|
AddButton("Quit", func() {
|
||||||
app.Stop()
|
app.Stop()
|
||||||
})
|
})
|
||||||
|
|
||||||
form.SetBorder(true).SetTitle("SSH Connection").SetTitleAlign(tview.AlignLeft)
|
layout := tview.NewFlex().
|
||||||
if err := app.SetRoot(form, true).Run(); err != nil {
|
SetDirection(tview.FlexRow).
|
||||||
panic(err)
|
AddItem(form, 0, 1, true).
|
||||||
|
AddItem(outputBox, 0, 1, false)
|
||||||
|
|
||||||
|
// Run the application
|
||||||
|
if err := app.SetRoot(layout, true).Run(); err != nil {
|
||||||
|
log.Fatalf("Error running application: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user