server.go

 1package main
 2
 3import (
 4	"fmt"
 5	"log"
 6	"path/filepath"
 7	"strings"
 8
 9	tea "github.com/charmbracelet/bubbletea"
10	"github.com/charmbracelet/charm/keygen"
11	"github.com/gliderlabs/ssh"
12	gossh "golang.org/x/crypto/ssh"
13)
14
15type Middleware func(ssh.Handler) ssh.Handler
16
17func LoggingMiddleware() Middleware {
18	return func(sh ssh.Handler) ssh.Handler {
19		return func(s ssh.Session) {
20			hpk := s.PublicKey() != nil
21			log.Printf("%s connect %v %v\n", s.RemoteAddr().String(), hpk, s.Command())
22			sh(s)
23			log.Printf("%s disconnect %v %v\n", s.RemoteAddr().String(), hpk, s.Command())
24		}
25	}
26}
27
28func BubbleTeaMiddleware(bth func(ssh.Session) tea.Model, opts ...tea.ProgramOption) Middleware {
29	return func(sh ssh.Handler) ssh.Handler {
30		return func(s ssh.Session) {
31			m := bth(s)
32			if m != nil {
33				opts = append(opts, tea.WithInput(s), tea.WithOutput(s))
34				p := tea.NewProgram(m, opts...)
35				err := p.Start()
36				if err != nil {
37					log.Printf("%s error %v: %s\n", s.RemoteAddr().String(), s.Command(), err)
38				}
39			}
40			sh(s)
41		}
42	}
43}
44
45type Server struct {
46	server *ssh.Server
47	key    gossh.PublicKey
48}
49
50func NewServer(port int, keyPath string, mw ...Middleware) (*Server, error) {
51	s := &Server{server: &ssh.Server{}}
52	s.server.Version = "OpenSSH_7.6p1"
53	s.server.Addr = fmt.Sprintf(":%d", port)
54	s.server.PasswordHandler = s.passHandler
55	s.server.PublicKeyHandler = s.authHandler
56	kps := strings.Split(keyPath, string(filepath.Separator))
57	kp := strings.Join(kps[:len(kps)-1], string(filepath.Separator))
58	n := strings.TrimRight(kps[len(kps)-1], "_ed25519")
59	_, err := keygen.NewSSHKeyPair(kp, n, nil, "ed25519")
60	if err != nil {
61		return nil, err
62	}
63	k := ssh.HostKeyFile(keyPath)
64	err = s.server.SetOption(k)
65	if err != nil {
66		return nil, err
67	}
68	h := func(s ssh.Session) {}
69	for _, m := range mw {
70		h = m(h)
71	}
72	s.server.Handler = h
73	return s, nil
74}
75
76func (srv *Server) sessionHandler(s ssh.Session) {
77}
78
79func (srv *Server) authHandler(ctx ssh.Context, key ssh.PublicKey) bool {
80	return true
81}
82
83func (srv *Server) passHandler(ctx ssh.Context, pass string) bool {
84	return true
85}
86
87func (srv *Server) Start() error {
88	return srv.server.ListenAndServe()
89}