Skip to content

Instantly share code, notes, and snippets.

@atotto
Last active May 30, 2024 20:05
Show Gist options
  • Save atotto/ba19155295d95c8d75881e145c751372 to your computer and use it in GitHub Desktop.
Save atotto/ba19155295d95c8d75881e145c751372 to your computer and use it in GitHub Desktop.
golang ssh terminal client
package main
import (
"context"
"flag"
"fmt"
"log"
"os"
"os/signal"
"syscall"
"time"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/terminal"
)
var (
user = flag.String("l", "", "login_name")
password = flag.String("pass", "", "password")
port = flag.Int("p", 22, "port")
)
func main() {
flag.Parse()
if flag.NArg() == 0 {
flag.Usage()
os.Exit(2)
}
sig := make(chan os.Signal, 1)
signal.Notify(sig, syscall.SIGTERM, syscall.SIGINT)
ctx, cancel := context.WithCancel(context.Background())
go func() {
if err := run(ctx); err != nil {
log.Print(err)
}
cancel()
}()
select {
case <-sig:
cancel()
case <-ctx.Done():
}
}
func run(ctx context.Context) error {
config := &ssh.ClientConfig{
User: *user,
Auth: []ssh.AuthMethod{
ssh.Password(*password),
},
Timeout: 5 * time.Second,
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
hostport := fmt.Sprintf("%s:%d", flag.Arg(0), *port)
conn, err := ssh.Dial("tcp", hostport, config)
if err != nil {
return fmt.Errorf("cannot connect %v: %v", hostport, err)
}
defer conn.Close()
session, err := conn.NewSession()
if err != nil {
return fmt.Errorf("cannot open new session: %v", err)
}
defer session.Close()
go func() {
<-ctx.Done()
conn.Close()
}()
fd := int(os.Stdin.Fd())
state, err := terminal.MakeRaw(fd)
if err != nil {
return fmt.Errorf("terminal make raw: %s", err)
}
defer terminal.Restore(fd, state)
w, h, err := terminal.GetSize(fd)
if err != nil {
return fmt.Errorf("terminal get size: %s", err)
}
modes := ssh.TerminalModes{
ssh.ECHO: 1,
ssh.TTY_OP_ISPEED: 14400,
ssh.TTY_OP_OSPEED: 14400,
}
term := os.Getenv("TERM")
if term == "" {
term = "xterm-256color"
}
if err := session.RequestPty(term, h, w, modes); err != nil {
return fmt.Errorf("session xterm: %s", err)
}
session.Stdout = os.Stdout
session.Stderr = os.Stderr
session.Stdin = os.Stdin
if err := session.Shell(); err != nil {
return fmt.Errorf("session shell: %s", err)
}
if err := session.Wait(); err != nil {
if e, ok := err.(*ssh.ExitError); ok {
switch e.ExitStatus() {
case 130:
return nil
}
}
return fmt.Errorf("ssh: %s", err)
}
return nil
}
@levidurfee
Copy link

Thank you for this :)

@shencan
Copy link

shencan commented Mar 26, 2021

How do I get terminal inputs and outputs for auditing

@jan4984
Copy link

jan4984 commented Aug 3, 2022

how to run shell commands then? input to session.Stdin?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment