blob: 2b1f9e8aaeb2ada45348fc55673443c131baa2e8 [file] [log] [blame]
Sean McCulloughbaa2b592025-04-23 10:40:08 -07001package server
2
3import (
4 "context"
5 "fmt"
6 "io"
7 "os"
8 "os/exec"
9 "syscall"
10 "unsafe"
11
12 "github.com/creack/pty"
13 "github.com/gliderlabs/ssh"
14)
15
16func setWinsize(f *os.File, w, h int) {
17 syscall.Syscall(syscall.SYS_IOCTL, f.Fd(), uintptr(syscall.TIOCSWINSZ),
18 uintptr(unsafe.Pointer(&struct{ h, w, x, y uint16 }{uint16(h), uint16(w), 0, 0})))
19}
20
21func (s *Server) ServeSSH(ctx context.Context, hostKey, authorizedKeys []byte) error {
22 allowed, _, _, _, err := ssh.ParseAuthorizedKey(authorizedKeys)
23 if err != nil {
24 return fmt.Errorf("ServeSSH: couldn't parse authorized keys: %w", err)
25 }
26 return ssh.ListenAndServe(":2022",
27 func(s ssh.Session) {
28 handleSessionfunc(ctx, s)
29 },
30 ssh.HostKeyPEM(hostKey),
31 ssh.PublicKeyAuth(func(ctx ssh.Context, key ssh.PublicKey) bool {
32 return ssh.KeysEqual(key, allowed)
33 }),
34 )
35}
36
37func handleSessionfunc(ctx context.Context, s ssh.Session) {
38 cmd := exec.CommandContext(ctx, "/bin/bash")
39 ptyReq, winCh, isPty := s.Pty()
40 if isPty {
41 cmd.Env = append(cmd.Env, fmt.Sprintf("TERM=%s", ptyReq.Term))
42 f, err := pty.Start(cmd)
43 if err != nil {
44 panic(err)
45 }
46 go func() {
47 for win := range winCh {
48 setWinsize(f, win.Width, win.Height)
49 }
50 }()
51 go func() {
52 io.Copy(f, s) // stdin
53 }()
54 io.Copy(s, f) // stdout
55 cmd.Wait()
56 } else {
57 io.WriteString(s, "No PTY requested.\n")
58 s.Exit(1)
59 }
60}