Skip to content

Instantly share code, notes, and snippets.

@atotto
Last active August 11, 2023 02:43
Show Gist options
  • Star 18 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • 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