| Sean McCullough | baa2b59 | 2025-04-23 10:40:08 -0700 | [diff] [blame^] | 1 | package server |
| 2 | |
| 3 | import ( |
| 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 | |
| 16 | func 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 | |
| 21 | func (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 | |
| 37 | func 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 | } |