SSH server entry point. #77
@ -6,13 +6,14 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"path"
|
"path"
|
||||||
"os"
|
"os"
|
||||||
|
"os/signal"
|
||||||
"sync"
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
"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.grassecon.net/urdt/ussd/internal/storage"
|
|
||||||
"git.grassecon.net/urdt/ussd/internal/ssh"
|
"git.grassecon.net/urdt/ussd/internal/ssh"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -47,9 +48,15 @@ func main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
logg.WarnCtxf(ctx, "!!!!! WARNING WARNING WARNING")
|
||||||
|
logg.WarnCtxf(ctx, "!!!!! =======================")
|
||||||
|
logg.WarnCtxf(ctx, "!!!!! This is not a production ready server!")
|
||||||
|
logg.WarnCtxf(ctx, "!!!!! Do not expose to internet and only use with tunnel!")
|
||||||
|
logg.WarnCtxf(ctx, "!!!!! (See ssh -L <...>)")
|
||||||
|
|
||||||
logg.Infof("start command", "dbdir", dbDir, "resourcedir", resourceDir, "outputsize", size, "keyfile", sshKeyFile, "host", host, "port", port)
|
logg.Infof("start command", "dbdir", dbDir, "resourcedir", resourceDir, "outputsize", size, "keyfile", sshKeyFile, "host", host, "port", port)
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
pfp := path.Join(scriptDir, "pp.csv")
|
pfp := path.Join(scriptDir, "pp.csv")
|
||||||
|
|
||||||
cfg := engine.Config{
|
cfg := engine.Config{
|
||||||
@ -64,20 +71,24 @@ func main() {
|
|||||||
cfg.EngineDebug = true
|
cfg.EngineDebug = true
|
||||||
}
|
}
|
||||||
|
|
||||||
keyStoreFile := path.Join(dbDir, "ssh_authorized_keys.gdbm")
|
authKeyStore, err := ssh.NewSshKeyStore(ctx, dbDir)
|
||||||
authKeyStore := storage.NewThreadGdbmDb()
|
|
||||||
err = authKeyStore.Connect(ctx, keyStoreFile)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "keystore file open error: %v", err)
|
fmt.Fprintf(os.Stderr, "keystore file open error: %v", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
defer func () {
|
defer func () {
|
||||||
err := authKeyStore.Close()
|
logg.TraceCtxf(ctx, "shutdown auth key store reached")
|
||||||
|
err = authKeyStore.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logg.ErrorCtxf(ctx, "keystore close error", "err", err)
|
logg.ErrorCtxf(ctx, "keystore close error", "err", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
cint := make(chan os.Signal)
|
||||||
|
cterm := make(chan os.Signal)
|
||||||
|
signal.Notify(cint, os.Interrupt, syscall.SIGINT)
|
||||||
|
signal.Notify(cterm, os.Interrupt, syscall.SIGTERM)
|
||||||
|
|
||||||
runner := &ssh.SshRunner{
|
runner := &ssh.SshRunner{
|
||||||
Cfg: cfg,
|
Cfg: cfg,
|
||||||
Debug: engineDebug,
|
Debug: engineDebug,
|
||||||
@ -88,5 +99,17 @@ func main() {
|
|||||||
Host: host,
|
Host: host,
|
||||||
Port: port,
|
Port: port,
|
||||||
}
|
}
|
||||||
|
go func() {
|
||||||
|
select {
|
||||||
|
case _ = <-cint:
|
||||||
|
case _ = <-cterm:
|
||||||
|
}
|
||||||
|
logg.TraceCtxf(ctx, "shutdown runner reached")
|
||||||
|
err := runner.Stop()
|
||||||
|
if err != nil {
|
||||||
|
logg.ErrorCtxf(ctx, "runner stop error", "err", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}()
|
||||||
runner.Run(ctx, authKeyStore)
|
runner.Run(ctx, authKeyStore)
|
||||||
}
|
}
|
||||||
|
44
cmd/ssh/sshkey/main.go
Normal file
44
cmd/ssh/sshkey/main.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"git.grassecon.net/urdt/ussd/internal/ssh"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var dbDir string
|
||||||
|
var sessionId string
|
||||||
|
flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from")
|
||||||
|
flag.StringVar(&sessionId, "i", "", "session id")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if sessionId == "" {
|
||||||
|
fmt.Fprintf(os.Stderr, "empty session id\n")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
sshKeyFile := flag.Arg(0)
|
||||||
|
if sshKeyFile == "" {
|
||||||
|
fmt.Fprintf(os.Stderr, "missing key file argument\n")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
store, err := ssh.NewSshKeyStore(ctx, dbDir)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
defer store.Close()
|
||||||
|
|
||||||
|
err = store.AddFromFile(ctx, sshKeyFile, sessionId)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
64
internal/ssh/keystore.go
Normal file
64
internal/ssh/keystore.go
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
package ssh
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/ssh"
|
||||||
|
|
||||||
|
"git.defalsify.org/vise.git/db"
|
||||||
|
|
||||||
|
"git.grassecon.net/urdt/ussd/internal/storage"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SshKeyStore struct {
|
||||||
|
store db.Db
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSshKeyStore(ctx context.Context, dbDir string) (*SshKeyStore, error) {
|
||||||
|
keyStore := &SshKeyStore{}
|
||||||
|
keyStoreFile := path.Join(dbDir, "ssh_authorized_keys.gdbm")
|
||||||
|
keyStore.store = storage.NewThreadGdbmDb()
|
||||||
|
err := keyStore.store.Connect(ctx, keyStoreFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return keyStore, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func(s *SshKeyStore) AddFromFile(ctx context.Context, fp string, sessionId string) error {
|
||||||
|
_, err := os.Stat(fp)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cannot open ssh server public key file: %v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
publicBytes, err := os.ReadFile(fp)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed to load public key: %v", err)
|
||||||
|
}
|
||||||
|
pubKey, _, _, _, err := ssh.ParseAuthorizedKey(publicBytes)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed to parse public key: %v", err)
|
||||||
|
}
|
||||||
|
k := append([]byte{0x01}, pubKey.Marshal()...)
|
||||||
|
s.store.SetPrefix(storage.DATATYPE_CUSTOM)
|
||||||
|
logg.Infof("Added key", "sessionId", sessionId, "public key", string(publicBytes))
|
||||||
|
return s.store.Put(ctx, k, []byte(sessionId))
|
||||||
|
}
|
||||||
|
|
||||||
|
func(s *SshKeyStore) Get(ctx context.Context, pubKey ssh.PublicKey) (string, error) {
|
||||||
|
s.store.SetLanguage(nil)
|
||||||
|
s.store.SetPrefix(storage.DATATYPE_CUSTOM)
|
||||||
|
k := append([]byte{0x01}, pubKey.Marshal()...)
|
||||||
|
v, err := s.store.Get(ctx, k)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(v), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func(s *SshKeyStore) Close() error {
|
||||||
|
return s.store.Close()
|
||||||
|
}
|
@ -11,7 +11,6 @@ import (
|
|||||||
|
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
|
|
||||||
"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/resource"
|
||||||
@ -27,11 +26,11 @@ var (
|
|||||||
|
|
||||||
type auther struct {
|
type auther struct {
|
||||||
Ctx context.Context
|
Ctx context.Context
|
||||||
keyStore db.Db
|
keyStore *SshKeyStore
|
||||||
auth map[string]string
|
auth map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAuther(ctx context.Context, keyStore db.Db) *auther {
|
func NewAuther(ctx context.Context, keyStore *SshKeyStore) *auther {
|
||||||
return &auther{
|
return &auther{
|
||||||
Ctx: ctx,
|
Ctx: ctx,
|
||||||
keyStore: keyStore,
|
keyStore: keyStore,
|
||||||
@ -40,17 +39,13 @@ func NewAuther(ctx context.Context, keyStore db.Db) *auther {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func(a *auther) Check(conn ssh.ConnMetadata, pubKey ssh.PublicKey) (*ssh.Permissions, error) {
|
func(a *auther) Check(conn ssh.ConnMetadata, pubKey ssh.PublicKey) (*ssh.Permissions, error) {
|
||||||
a.keyStore.SetLanguage(nil)
|
va, err := a.keyStore.Get(a.Ctx, pubKey)
|
||||||
a.keyStore.SetPrefix(storage.DATATYPE_CUSTOM)
|
|
||||||
k := append([]byte{0x01}, pubKey.Marshal()...)
|
|
||||||
v, err := a.keyStore.Get(a.Ctx, k)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
ka := hex.EncodeToString(conn.SessionID())
|
ka := hex.EncodeToString(conn.SessionID())
|
||||||
va := string(v)
|
|
||||||
a.auth[ka] = va
|
a.auth[ka] = va
|
||||||
fmt.Fprintf(os.Stderr, "connect: %s -> %s\n", ka, v)
|
fmt.Fprintf(os.Stderr, "connect: %s -> %s\n", ka, va)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,6 +137,11 @@ type SshRunner struct {
|
|||||||
Host string
|
Host string
|
||||||
Port uint
|
Port uint
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
|
lst net.Listener
|
||||||
|
}
|
||||||
|
|
||||||
|
func(s *SshRunner) Stop() error {
|
||||||
|
return s.lst.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func(s *SshRunner) GetEngine(sessionId string) (engine.Engine, func(), error) {
|
func(s *SshRunner) GetEngine(sessionId string) (engine.Engine, func(), error) {
|
||||||
@ -203,7 +203,7 @@ func(s *SshRunner) GetEngine(sessionId string) (engine.Engine, func(), error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// adapted example from crypto/ssh package, NewServerConn doc
|
// adapted example from crypto/ssh package, NewServerConn doc
|
||||||
func(s *SshRunner) Run(ctx context.Context, keyStore db.Db) {
|
func(s *SshRunner) Run(ctx context.Context, keyStore *SshKeyStore) {
|
||||||
running := true
|
running := true
|
||||||
|
|
||||||
// TODO: waitgroup should probably not be global
|
// TODO: waitgroup should probably not be global
|
||||||
@ -224,18 +224,19 @@ func(s *SshRunner) Run(ctx context.Context, keyStore db.Db) {
|
|||||||
}
|
}
|
||||||
cfg.AddHostKey(private)
|
cfg.AddHostKey(private)
|
||||||
|
|
||||||
lst, err := net.Listen("tcp", fmt.Sprintf("%s:%d", s.Host, s.Port))
|
s.lst, err = net.Listen("tcp", fmt.Sprintf("%s:%d", s.Host, s.Port))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for running {
|
for running {
|
||||||
conn, err := lst.Accept()
|
conn, err := s.lst.Accept()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
logg.ErrorCtxf(ctx, "ssh accept error", "err", err)
|
||||||
|
running = false
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
go func(conn net.Conn) {
|
go func(conn net.Conn) {
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
for true {
|
for true {
|
||||||
|
Loading…
Reference in New Issue
Block a user