forked from urdt/ussd
Factor out ssh service code
This commit is contained in:
parent
19372c17f4
commit
935b777e57
297
cmd/ssh/main.go
297
cmd/ssh/main.go
@ -2,25 +2,18 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/hex"
|
|
||||||
"errors"
|
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
|
||||||
"path"
|
"path"
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"golang.org/x/crypto/ssh"
|
|
||||||
|
|
||||||
"git.defalsify.org/vise.git/db"
|
"git.defalsify.org/vise.git/db"
|
||||||
"git.defalsify.org/vise.git/engine"
|
"git.defalsify.org/vise.git/engine"
|
||||||
"git.defalsify.org/vise.git/logging"
|
"git.defalsify.org/vise.git/logging"
|
||||||
"git.defalsify.org/vise.git/resource"
|
|
||||||
"git.defalsify.org/vise.git/state"
|
|
||||||
|
|
||||||
"git.grassecon.net/urdt/ussd/internal/handlers"
|
|
||||||
"git.grassecon.net/urdt/ussd/internal/storage"
|
"git.grassecon.net/urdt/ussd/internal/storage"
|
||||||
|
"git.grassecon.net/urdt/ussd/internal/ssh"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -30,274 +23,6 @@ var (
|
|||||||
scriptDir = path.Join("services", "registration")
|
scriptDir = path.Join("services", "registration")
|
||||||
)
|
)
|
||||||
|
|
||||||
type auther struct {
|
|
||||||
Ctx context.Context
|
|
||||||
auth map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewAuther(ctx context.Context) *auther {
|
|
||||||
return &auther{
|
|
||||||
Ctx: ctx,
|
|
||||||
auth: make(map[string]string),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func(a *auther) Check(conn ssh.ConnMetadata, pubKey ssh.PublicKey) (*ssh.Permissions, error) {
|
|
||||||
keyStore.SetLanguage(nil)
|
|
||||||
keyStore.SetPrefix(storage.DATATYPE_CUSTOM)
|
|
||||||
k := append([]byte{0x01}, pubKey.Marshal()...)
|
|
||||||
v, err := keyStore.Get(a.Ctx, k)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ka := hex.EncodeToString(conn.SessionID())
|
|
||||||
va := string(v)
|
|
||||||
a.auth[ka] = va
|
|
||||||
fmt.Fprintf(os.Stderr, "connect: %s -> %s\n", ka, v)
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func(a *auther) FromConn(c *ssh.ServerConn) (string, error) {
|
|
||||||
if c == nil {
|
|
||||||
return "", errors.New("nil server conn")
|
|
||||||
}
|
|
||||||
if c.Conn == nil {
|
|
||||||
return "", errors.New("nil underlying conn")
|
|
||||||
}
|
|
||||||
return a.Get(c.Conn.SessionID())
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func(a *auther) Get(k []byte) (string, error) {
|
|
||||||
ka := hex.EncodeToString(k)
|
|
||||||
v, ok := a.auth[ka]
|
|
||||||
if !ok {
|
|
||||||
return "", errors.New("not found")
|
|
||||||
}
|
|
||||||
return v, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
//func serve(ctx context.Context, sessionId string, ch ssh.NewChannel, mss *storage.MenuStorageService, lhs *handlers.LocalHandlerService) error {
|
|
||||||
func serve(ctx context.Context, sessionId string, ch ssh.NewChannel, en engine.Engine) error {
|
|
||||||
if ch == nil {
|
|
||||||
return errors.New("nil channel")
|
|
||||||
}
|
|
||||||
if ch.ChannelType() != "session" {
|
|
||||||
ch.Reject(ssh.UnknownChannelType, "that is not the channel you are looking for")
|
|
||||||
return errors.New("not a session")
|
|
||||||
}
|
|
||||||
channel, requests, err := ch.Accept()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
defer channel.Close()
|
|
||||||
wg.Add(1)
|
|
||||||
go func(reqIn <-chan *ssh.Request) {
|
|
||||||
defer wg.Done()
|
|
||||||
for req := range reqIn {
|
|
||||||
req.Reply(req.Type == "shell", nil)
|
|
||||||
}
|
|
||||||
_ = requests
|
|
||||||
}(requests)
|
|
||||||
|
|
||||||
cont, err := en.Exec(ctx, []byte{})
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("initial engine exec err: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var input [state.INPUT_LIMIT]byte
|
|
||||||
for cont {
|
|
||||||
c, err := en.Flush(ctx, channel)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("flush err: %v", err)
|
|
||||||
}
|
|
||||||
_, err = channel.Write([]byte{0x0a})
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("newline err: %v", err)
|
|
||||||
}
|
|
||||||
c, err = channel.Read(input[:])
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("read input fail: %v", err)
|
|
||||||
}
|
|
||||||
logg.TraceCtxf(ctx, "input read", "c", c, "input", input[:c-1])
|
|
||||||
cont, err = en.Exec(ctx, input[:c-1])
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("engine exec err: %v", err)
|
|
||||||
}
|
|
||||||
logg.TraceCtxf(ctx, "exec cont", "cont", cont, "en", en)
|
|
||||||
_ = c
|
|
||||||
}
|
|
||||||
c, err := en.Flush(ctx, channel)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("last flush err: %v", err)
|
|
||||||
}
|
|
||||||
_ = c
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type sshRunner struct {
|
|
||||||
Ctx context.Context
|
|
||||||
Cfg engine.Config
|
|
||||||
FlagFile string
|
|
||||||
DbDir string
|
|
||||||
ResourceDir string
|
|
||||||
Debug bool
|
|
||||||
KeyFile string
|
|
||||||
Host string
|
|
||||||
Port uint
|
|
||||||
}
|
|
||||||
|
|
||||||
func(s *sshRunner) GetEngine(sessionId string) (engine.Engine, func(), error) {
|
|
||||||
ctx := s.Ctx
|
|
||||||
menuStorageService := storage.NewMenuStorageService(s.DbDir, s.ResourceDir)
|
|
||||||
|
|
||||||
err := menuStorageService.EnsureDbDir()
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
rs, err := menuStorageService.GetResource(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
pe, err := menuStorageService.GetPersister(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
userdatastore, err := menuStorageService.GetUserdataDb(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
dbResource, ok := rs.(*resource.DbResource)
|
|
||||||
if !ok {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
lhs, err := handlers.NewLocalHandlerService(s.FlagFile, true, dbResource, s.Cfg, rs)
|
|
||||||
lhs.SetDataStore(&userdatastore)
|
|
||||||
lhs.SetPersister(pe)
|
|
||||||
lhs.Cfg.SessionId = sessionId
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
hl, err := lhs.GetHandler()
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
en := lhs.GetEngine()
|
|
||||||
en = en.WithFirst(hl.Init)
|
|
||||||
if s.Debug {
|
|
||||||
en = en.WithDebug(nil)
|
|
||||||
}
|
|
||||||
// TODO: this is getting very hacky!
|
|
||||||
closer := func() {
|
|
||||||
err := menuStorageService.Close()
|
|
||||||
if err != nil {
|
|
||||||
logg.ErrorCtxf(ctx, "menu storage service cleanup fail", "err", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return en, closer, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// adapted example from crypto/ssh package, NewServerConn doc
|
|
||||||
func(s *sshRunner) Run(ctx context.Context) {
|
|
||||||
running := true
|
|
||||||
|
|
||||||
// TODO: waitgroup should probably not be global
|
|
||||||
defer wg.Wait()
|
|
||||||
|
|
||||||
auth := NewAuther(ctx)
|
|
||||||
cfg := ssh.ServerConfig{
|
|
||||||
PublicKeyCallback: auth.Check,
|
|
||||||
}
|
|
||||||
|
|
||||||
privateBytes, err := os.ReadFile(s.KeyFile)
|
|
||||||
if err != nil {
|
|
||||||
logg.ErrorCtxf(ctx, "Failed to load private key", "err", err)
|
|
||||||
}
|
|
||||||
private, err := ssh.ParsePrivateKey(privateBytes)
|
|
||||||
if err != nil {
|
|
||||||
logg.ErrorCtxf(ctx, "Failed to parse private key", "err", err)
|
|
||||||
}
|
|
||||||
cfg.AddHostKey(private)
|
|
||||||
|
|
||||||
lst, err := net.Listen("tcp", fmt.Sprintf("%s:%d", s.Host, s.Port))
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for running {
|
|
||||||
conn, err := lst.Accept()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
go func(conn net.Conn) {
|
|
||||||
defer conn.Close()
|
|
||||||
for true {
|
|
||||||
srvConn, nC, rC, err := ssh.NewServerConn(conn, &cfg)
|
|
||||||
if err != nil {
|
|
||||||
logg.InfoCtxf(ctx, "rejected client", "err", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
logg.DebugCtxf(ctx, "ssh client connected", "conn", srvConn)
|
|
||||||
|
|
||||||
wg.Add(1)
|
|
||||||
go func() {
|
|
||||||
ssh.DiscardRequests(rC)
|
|
||||||
wg.Done()
|
|
||||||
}()
|
|
||||||
|
|
||||||
sessionId, err := auth.FromConn(srvConn)
|
|
||||||
if err != nil {
|
|
||||||
logg.ErrorCtxf(ctx, "Cannot find authentication")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
en, closer, err := s.GetEngine(sessionId)
|
|
||||||
if err != nil {
|
|
||||||
logg.ErrorCtxf(ctx, "engine won't start", "err", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
err := en.Finish()
|
|
||||||
if err != nil {
|
|
||||||
logg.ErrorCtxf(ctx, "engine won't stop", "err", err)
|
|
||||||
}
|
|
||||||
closer()
|
|
||||||
}()
|
|
||||||
for ch := range nC {
|
|
||||||
err = serve(ctx, sessionId, ch, en)
|
|
||||||
logg.ErrorCtxf(ctx, "ssh server finish", "err", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}(conn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: This is test code, move to external tool for adding and removing keys
|
|
||||||
func sshLoadKeys(ctx context.Context, dbDir string) error {
|
|
||||||
keyStoreFile := path.Join(dbDir, "ssh_authorized_keys.gdbm")
|
|
||||||
keyStore = storage.NewThreadGdbmDb()
|
|
||||||
err := keyStore.Connect(ctx, keyStoreFile)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
pubKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte("ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCu5rYCxMBsVAL1TEkMQgmElAYEZj5zYDdyHjUxZ6qzHBOZD9GAzdxx9GyQDx2vdYm3329tLH/69ky1YA3nUz8SnJGBD6hC5XrqwN6zo9R9oOHAKTwiPGhey2NTVmheP+9XNHukBnOlkkWOQlpDDvMbWOztaZOWDaA8OIeP0t6qzFqLyelyg65lxzM3BKd7bCmmfzl/64BcP1MotAmB9DUxmY0Wb4Q2hYZfNYBx50Z4xthTgKV+Xoo8CbTduKotIz6hluQGvWdtxlCJQEiZ2f4RYY87JSA6/BAH2fhxuLHMXRpzocJNqARqCWpdcTGSg7bzxbKvTFH9OU4wZtr9ie40OR4zsc1lOBZL0rnp8GLkG8ZmeBQrgEDlmR9TTlz4okgtL+c5TCS37rjZYVjmtGwihws0EL9+wyv2dSQibirklC4wK5eWHKXl5vab19qzw/qRLdoRBK40DxbRKggxA7gqSsKrmrf+z7CuLIz/kxF+169FBLbh1MfBOGdx1awm6aU= lash@furioso"))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
k := append([]byte{0x01}, pubKey.Marshal()...)
|
|
||||||
keyStore.SetPrefix(storage.DATATYPE_CUSTOM)
|
|
||||||
return keyStore.Put(ctx, k, []byte("+25113243546"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var dbDir string
|
var dbDir string
|
||||||
var resourceDir string
|
var resourceDir string
|
||||||
@ -338,22 +63,30 @@ func main() {
|
|||||||
if engineDebug {
|
if engineDebug {
|
||||||
cfg.EngineDebug = true
|
cfg.EngineDebug = true
|
||||||
}
|
}
|
||||||
|
|
||||||
err = sshLoadKeys(ctx, dbDir)
|
keyStoreFile := path.Join(dbDir, "ssh_authorized_keys.gdbm")
|
||||||
|
authKeyStore := storage.NewThreadGdbmDb()
|
||||||
|
err = authKeyStore.Connect(ctx, keyStoreFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, err.Error())
|
fmt.Fprintf(os.Stderr, "keystore file open error: %v", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
defer func() {
|
||||||
|
err := authKeyStore.Close()
|
||||||
|
if err != nil {
|
||||||
|
logg.ErrorCtxf(ctx, "keystore close error", "err", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
runner := &sshRunner{
|
runner := &ssh.SshRunner{
|
||||||
Cfg: cfg,
|
Cfg: cfg,
|
||||||
Debug: engineDebug,
|
Debug: engineDebug,
|
||||||
FlagFile: pfp,
|
FlagFile: pfp,
|
||||||
DbDir: dbDir,
|
DbDir: dbDir,
|
||||||
ResourceDir: resourceDir,
|
ResourceDir: resourceDir,
|
||||||
KeyFile: sshKeyFile,
|
SrvKeyFile: sshKeyFile,
|
||||||
Host: host,
|
Host: host,
|
||||||
Port: port,
|
Port: port,
|
||||||
}
|
}
|
||||||
runner.Run(ctx)
|
runner.Run(ctx, authKeyStore)
|
||||||
}
|
}
|
||||||
|
279
internal/ssh/ssh.go
Normal file
279
internal/ssh/ssh.go
Normal file
@ -0,0 +1,279 @@
|
|||||||
|
package ssh
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/ssh"
|
||||||
|
|
||||||
|
"git.defalsify.org/vise.git/db"
|
||||||
|
"git.defalsify.org/vise.git/engine"
|
||||||
|
"git.defalsify.org/vise.git/logging"
|
||||||
|
"git.defalsify.org/vise.git/resource"
|
||||||
|
"git.defalsify.org/vise.git/state"
|
||||||
|
|
||||||
|
"git.grassecon.net/urdt/ussd/internal/handlers"
|
||||||
|
"git.grassecon.net/urdt/ussd/internal/storage"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
logg = logging.NewVanilla().WithDomain("ssh")
|
||||||
|
)
|
||||||
|
|
||||||
|
type auther struct {
|
||||||
|
Ctx context.Context
|
||||||
|
keyStore db.Db
|
||||||
|
auth map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAuther(ctx context.Context, keyStore db.Db) *auther {
|
||||||
|
return &auther{
|
||||||
|
Ctx: ctx,
|
||||||
|
keyStore: keyStore,
|
||||||
|
auth: make(map[string]string),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func(a *auther) Check(conn ssh.ConnMetadata, pubKey ssh.PublicKey) (*ssh.Permissions, error) {
|
||||||
|
a.keyStore.SetLanguage(nil)
|
||||||
|
a.keyStore.SetPrefix(storage.DATATYPE_CUSTOM)
|
||||||
|
k := append([]byte{0x01}, pubKey.Marshal()...)
|
||||||
|
v, err := a.keyStore.Get(a.Ctx, k)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ka := hex.EncodeToString(conn.SessionID())
|
||||||
|
va := string(v)
|
||||||
|
a.auth[ka] = va
|
||||||
|
fmt.Fprintf(os.Stderr, "connect: %s -> %s\n", ka, v)
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func(a *auther) FromConn(c *ssh.ServerConn) (string, error) {
|
||||||
|
if c == nil {
|
||||||
|
return "", errors.New("nil server conn")
|
||||||
|
}
|
||||||
|
if c.Conn == nil {
|
||||||
|
return "", errors.New("nil underlying conn")
|
||||||
|
}
|
||||||
|
return a.Get(c.Conn.SessionID())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func(a *auther) Get(k []byte) (string, error) {
|
||||||
|
ka := hex.EncodeToString(k)
|
||||||
|
v, ok := a.auth[ka]
|
||||||
|
if !ok {
|
||||||
|
return "", errors.New("not found")
|
||||||
|
}
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func(s *SshRunner) serve(ctx context.Context, sessionId string, ch ssh.NewChannel, en engine.Engine) error {
|
||||||
|
if ch == nil {
|
||||||
|
return errors.New("nil channel")
|
||||||
|
}
|
||||||
|
if ch.ChannelType() != "session" {
|
||||||
|
ch.Reject(ssh.UnknownChannelType, "that is not the channel you are looking for")
|
||||||
|
return errors.New("not a session")
|
||||||
|
}
|
||||||
|
channel, requests, err := ch.Accept()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer channel.Close()
|
||||||
|
s.wg.Add(1)
|
||||||
|
go func(reqIn <-chan *ssh.Request) {
|
||||||
|
defer s.wg.Done()
|
||||||
|
for req := range reqIn {
|
||||||
|
req.Reply(req.Type == "shell", nil)
|
||||||
|
}
|
||||||
|
_ = requests
|
||||||
|
}(requests)
|
||||||
|
|
||||||
|
cont, err := en.Exec(ctx, []byte{})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("initial engine exec err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var input [state.INPUT_LIMIT]byte
|
||||||
|
for cont {
|
||||||
|
c, err := en.Flush(ctx, channel)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("flush err: %v", err)
|
||||||
|
}
|
||||||
|
_, err = channel.Write([]byte{0x0a})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("newline err: %v", err)
|
||||||
|
}
|
||||||
|
c, err = channel.Read(input[:])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("read input fail: %v", err)
|
||||||
|
}
|
||||||
|
logg.TraceCtxf(ctx, "input read", "c", c, "input", input[:c-1])
|
||||||
|
cont, err = en.Exec(ctx, input[:c-1])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("engine exec err: %v", err)
|
||||||
|
}
|
||||||
|
logg.TraceCtxf(ctx, "exec cont", "cont", cont, "en", en)
|
||||||
|
_ = c
|
||||||
|
}
|
||||||
|
c, err := en.Flush(ctx, channel)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("last flush err: %v", err)
|
||||||
|
}
|
||||||
|
_ = c
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type SshRunner struct {
|
||||||
|
Ctx context.Context
|
||||||
|
Cfg engine.Config
|
||||||
|
FlagFile string
|
||||||
|
DbDir string
|
||||||
|
ResourceDir string
|
||||||
|
Debug bool
|
||||||
|
SrvKeyFile string
|
||||||
|
Host string
|
||||||
|
Port uint
|
||||||
|
wg sync.WaitGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
func(s *SshRunner) GetEngine(sessionId string) (engine.Engine, func(), error) {
|
||||||
|
ctx := s.Ctx
|
||||||
|
menuStorageService := storage.NewMenuStorageService(s.DbDir, s.ResourceDir)
|
||||||
|
|
||||||
|
err := menuStorageService.EnsureDbDir()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rs, err := menuStorageService.GetResource(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pe, err := menuStorageService.GetPersister(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
userdatastore, err := menuStorageService.GetUserdataDb(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
dbResource, ok := rs.(*resource.DbResource)
|
||||||
|
if !ok {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
lhs, err := handlers.NewLocalHandlerService(s.FlagFile, true, dbResource, s.Cfg, rs)
|
||||||
|
lhs.SetDataStore(&userdatastore)
|
||||||
|
lhs.SetPersister(pe)
|
||||||
|
lhs.Cfg.SessionId = sessionId
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
hl, err := lhs.GetHandler()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
en := lhs.GetEngine()
|
||||||
|
en = en.WithFirst(hl.Init)
|
||||||
|
if s.Debug {
|
||||||
|
en = en.WithDebug(nil)
|
||||||
|
}
|
||||||
|
// TODO: this is getting very hacky!
|
||||||
|
closer := func() {
|
||||||
|
err := menuStorageService.Close()
|
||||||
|
if err != nil {
|
||||||
|
logg.ErrorCtxf(ctx, "menu storage service cleanup fail", "err", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return en, closer, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// adapted example from crypto/ssh package, NewServerConn doc
|
||||||
|
func(s *SshRunner) Run(ctx context.Context, keyStore db.Db) {
|
||||||
|
running := true
|
||||||
|
|
||||||
|
// TODO: waitgroup should probably not be global
|
||||||
|
defer s.wg.Wait()
|
||||||
|
|
||||||
|
auth := NewAuther(ctx, keyStore)
|
||||||
|
cfg := ssh.ServerConfig{
|
||||||
|
PublicKeyCallback: auth.Check,
|
||||||
|
}
|
||||||
|
|
||||||
|
privateBytes, err := os.ReadFile(s.SrvKeyFile)
|
||||||
|
if err != nil {
|
||||||
|
logg.ErrorCtxf(ctx, "Failed to load private key", "err", err)
|
||||||
|
}
|
||||||
|
private, err := ssh.ParsePrivateKey(privateBytes)
|
||||||
|
if err != nil {
|
||||||
|
logg.ErrorCtxf(ctx, "Failed to parse private key", "err", err)
|
||||||
|
}
|
||||||
|
cfg.AddHostKey(private)
|
||||||
|
|
||||||
|
lst, err := net.Listen("tcp", fmt.Sprintf("%s:%d", s.Host, s.Port))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for running {
|
||||||
|
conn, err := lst.Accept()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
go func(conn net.Conn) {
|
||||||
|
defer conn.Close()
|
||||||
|
for true {
|
||||||
|
srvConn, nC, rC, err := ssh.NewServerConn(conn, &cfg)
|
||||||
|
if err != nil {
|
||||||
|
logg.InfoCtxf(ctx, "rejected client", "err", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logg.DebugCtxf(ctx, "ssh client connected", "conn", srvConn)
|
||||||
|
|
||||||
|
s.wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
ssh.DiscardRequests(rC)
|
||||||
|
s.wg.Done()
|
||||||
|
}()
|
||||||
|
|
||||||
|
sessionId, err := auth.FromConn(srvConn)
|
||||||
|
if err != nil {
|
||||||
|
logg.ErrorCtxf(ctx, "Cannot find authentication")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
en, closer, err := s.GetEngine(sessionId)
|
||||||
|
if err != nil {
|
||||||
|
logg.ErrorCtxf(ctx, "engine won't start", "err", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
err := en.Finish()
|
||||||
|
if err != nil {
|
||||||
|
logg.ErrorCtxf(ctx, "engine won't stop", "err", err)
|
||||||
|
}
|
||||||
|
closer()
|
||||||
|
}()
|
||||||
|
for ch := range nC {
|
||||||
|
err = s.serve(ctx, sessionId, ch, en)
|
||||||
|
logg.ErrorCtxf(ctx, "ssh server finish", "err", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}(conn)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user