From e0bcad8ab49abea4c92c7014cdaf366898d0c44a Mon Sep 17 00:00:00 2001 From: Erik Brakkee Date: Thu, 18 Jul 2024 19:00:30 +0200 Subject: [PATCH] first commit --- go.mod | 17 +++++++ go.sum | 54 +++++++++++++++++++++ sshclient.go | 134 +++++++++++++++++++++++++++++++++++++++++++++++++++ sshserver.go | 83 +++++++++++++++++++++++++++++++ 4 files changed, 288 insertions(+) create mode 100755 go.mod create mode 100755 go.sum create mode 100755 sshclient.go create mode 100755 sshserver.go diff --git a/go.mod b/go.mod new file mode 100755 index 0000000..bb6174d --- /dev/null +++ b/go.mod @@ -0,0 +1,17 @@ +module ssh + +go 1.18 + +require ( + github.com/creack/pty v1.1.21 + github.com/gliderlabs/ssh v0.3.7 + github.com/pkg/sftp v1.13.6 + golang.org/x/crypto v0.25.0 + golang.org/x/term v0.22.0 +) + +require ( + github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect + github.com/kr/fs v0.1.0 // indirect + golang.org/x/sys v0.22.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100755 index 0000000..3b5301e --- /dev/null +++ b/go.sum @@ -0,0 +1,54 @@ +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= +github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0= +github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE= +github.com/gliderlabs/ssh v0.3.7/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8= +github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/pkg/sftp v1.13.6 h1:JFZT4XbOU7l77xGSpOdW+pwIMqP044IyjXX6FGyEKFo= +github.com/pkg/sftp v1.13.6/go.mod h1:tz1ryNURKu77RL+GuCzmoJYxQczL3wLNNpPWagdg4Qk= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= +golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/sshclient.go b/sshclient.go new file mode 100755 index 0000000..302b51d --- /dev/null +++ b/sshclient.go @@ -0,0 +1,134 @@ +package main + +import ( + "fmt" + "log" + "net" + "os" + "os/signal" + "syscall" + + "golang.org/x/crypto/ssh" + "golang.org/x/term" +) + +func main() { + if len(os.Args) != 3 { + fmt.Println("Usage: go run ssh_client.go ") + os.Exit(1) + } + + user := os.Args[1] + address := os.Args[2] + + fmt.Print("Enter Password: ") + password, err := term.ReadPassword(int(os.Stdin.Fd())) + if err != nil { + log.Fatalf("Failed to read password: %v", err) + } + fmt.Println() + + // Establish TCP connection + netConn, err := net.Dial("tcp", address) + if err != nil { + log.Fatalf("Failed to connect to %s: %v", address, err) + } + + // Use the connection to create an SSH client + err = runSSHClient(user, string(password), netConn) + if err != nil { + log.Fatalf("SSH session failed: %v", err) + } +} + +func runSSHClient(user, password string, netConn net.Conn) error { + config := &ssh.ClientConfig{ + User: user, + Auth: []ssh.AuthMethod{ + ssh.Password(password), + }, + HostKeyCallback: ssh.InsecureIgnoreHostKey(), + } + + // Create SSH connection from net.Conn + conn, chans, reqs, err := ssh.NewClientConn(netConn, netConn.RemoteAddr().String(), config) + if err != nil { + return fmt.Errorf("failed to create SSH client connection: %v", err) + } + + client := ssh.NewClient(conn, chans, reqs) + defer client.Close() + + session, err := client.NewSession() + if err != nil { + return fmt.Errorf("failed to create session: %v", err) + } + defer session.Close() + + modes := ssh.TerminalModes{ + ssh.ECHO: 1, + ssh.TTY_OP_ISPEED: 14400, + ssh.TTY_OP_OSPEED: 14400, + } + + fd := int(os.Stdin.Fd()) + oldState, err := term.MakeRaw(fd) + if err != nil { + return fmt.Errorf("failed to set raw mode: %v", err) + } + defer term.Restore(fd, oldState) + + width, height, err := term.GetSize(fd) + if err != nil { + return fmt.Errorf("failed to get terminal size: %v", err) + } + + if err := session.RequestPty("xterm", height, width, modes); err != nil { + return fmt.Errorf("request for pseudo terminal failed: %v", err) + } + + session.Stdout = os.Stdout + session.Stderr = os.Stderr + session.Stdin = os.Stdin + + if err := session.Shell(); err != nil { + return fmt.Errorf("failed to start shell: %v", err) + } + + // Handle window size changes + go handleWindowChange(session, fd) + + // Handle Ctrl+C (SIGINT) + sigChan := make(chan os.Signal, 1) + signal.Notify(sigChan, syscall.SIGINT) + go func() { + for range sigChan { + session.Signal(ssh.SIGINT) + } + }() + + if err := session.Wait(); err != nil { + if e, ok := err.(*ssh.ExitError); ok { + os.Exit(e.ExitStatus()) + } + return fmt.Errorf("failed to wait for session: %v", err) + } + + return nil +} + +func handleWindowChange(session *ssh.Session, fd int) { + sigwinchCh := make(chan os.Signal, 1) + signal.Notify(sigwinchCh, syscall.SIGWINCH) + + for range sigwinchCh { + width, height, err := term.GetSize(fd) + if err != nil { + log.Printf("Failed to get window size: %v", err) + continue + } + if err := session.WindowChange(height, width); err != nil { + log.Printf("Failed to send window change request: %v", err) + } + } +} diff --git a/sshserver.go b/sshserver.go new file mode 100755 index 0000000..1d1f8a6 --- /dev/null +++ b/sshserver.go @@ -0,0 +1,83 @@ +package main + +import ( + "fmt" + "io" + "log" + "os" + "os/exec" + "syscall" + "unsafe" + + "github.com/creack/pty" + "github.com/gliderlabs/ssh" + "github.com/pkg/sftp" +) + +func SftpHandler(sess ssh.Session) { + debugStream := io.Discard + serverOptions := []sftp.ServerOption{ + sftp.WithDebug(debugStream), + } + server, err := sftp.NewServer( + sess, + serverOptions..., + ) + if err != nil { + log.Printf("sftp server init error: %s\n", err) + return + } + if err := server.Serve(); err == io.EOF { + server.Close() + fmt.Println("sftp client exited session.") + } else if err != nil { + fmt.Println("sftp server completed with error:", err) + } +} + +func passwordAuth(ctx ssh.Context, password string) bool { + // Replace with your own logic to validate username and password + return ctx.User() == "abc" && password == "123" +} + +func setWinsize(f *os.File, w, h int) { + syscall.Syscall(syscall.SYS_IOCTL, f.Fd(), uintptr(syscall.TIOCSWINSZ), + uintptr(unsafe.Pointer(&struct{ h, w, x, y uint16 }{uint16(h), uint16(w), 0, 0}))) +} + +func main() { + ssh.Handle(func(s ssh.Session) { + cmd := exec.Command("bash") + ptyReq, winCh, isPty := s.Pty() + if isPty { + cmd.Env = append(os.Environ(), fmt.Sprintf("TERM=%s", ptyReq.Term)) + f, err := pty.Start(cmd) + if err != nil { + panic(err) + } + go func() { + for win := range winCh { + setWinsize(f, win.Width, win.Height) + } + }() + go func() { + io.Copy(f, s) // stdin + }() + io.Copy(s, f) // stdout + cmd.Wait() + } else { + io.WriteString(s, "No PTY requested.\n") + s.Exit(1) + } + }) + + log.Println("starting ssh server on port 2222...") + server := ssh.Server{ + Addr: ":2222", + PasswordHandler: passwordAuth, + SubsystemHandlers: map[string]ssh.SubsystemHandler{ + "sftp": SftpHandler, + }, + } + log.Fatal(server.ListenAndServe()) +}