diff options
author | Runxi Yu <me@runxiyu.org> | 2025-08-12 11:01:07 +0800 |
---|---|---|
committer | Runxi Yu <me@runxiyu.org> | 2025-09-13 19:08:22 +0800 |
commit | 5717faed659a9eeb86c528ab56822c42eca1ad3f (patch) | |
tree | 92e6662628a51c03c52300d2fd98173716a82882 /forged/internal/incoming/ssh/ssh.go | |
parent | Remove forge-specific functions from misc (diff) | |
download | forge-5717faed659a9eeb86c528ab56822c42eca1ad3f.tar.gz forge-5717faed659a9eeb86c528ab56822c42eca1ad3f.tar.zst forge-5717faed659a9eeb86c528ab56822c42eca1ad3f.zip |
Refactor
Diffstat (limited to 'forged/internal/incoming/ssh/ssh.go')
-rw-r--r-- | forged/internal/incoming/ssh/ssh.go | 89 |
1 files changed, 89 insertions, 0 deletions
diff --git a/forged/internal/incoming/ssh/ssh.go b/forged/internal/incoming/ssh/ssh.go new file mode 100644 index 0000000..527cd28 --- /dev/null +++ b/forged/internal/incoming/ssh/ssh.go @@ -0,0 +1,89 @@ +package ssh + +import ( + "context" + "errors" + "fmt" + "os" + "time" + + gliderssh "github.com/gliderlabs/ssh" + "go.lindenii.runxiyu.org/forge/forged/internal/common/misc" + "go.lindenii.runxiyu.org/forge/forged/internal/global" + gossh "golang.org/x/crypto/ssh" +) + +type Server struct { + gliderServer *gliderssh.Server + privkey gossh.Signer + net string + addr string + root string + shutdownTimeout uint32 + global *global.Global +} + +func New(config Config, global *global.Global) (server *Server, err error) { + server = &Server{ + net: config.Net, + addr: config.Addr, + root: config.Root, + shutdownTimeout: config.ShutdownTimeout, + global: global, + } //exhaustruct:ignore + + var privkeyBytes []byte + + privkeyBytes, err = os.ReadFile(config.Key) + if err != nil { + return server, fmt.Errorf("read SSH private key: %w", err) + } + + server.privkey, err = gossh.ParsePrivateKey(privkeyBytes) + if err != nil { + return server, fmt.Errorf("parse SSH private key: %w", err) + } + + server.global.SSHPubkey = misc.BytesToString(gossh.MarshalAuthorizedKey(server.privkey.PublicKey())) + server.global.SSHFingerprint = gossh.FingerprintSHA256(server.privkey.PublicKey()) + + server.gliderServer = &gliderssh.Server{ + Handler: handle, + PublicKeyHandler: func(ctx gliderssh.Context, key gliderssh.PublicKey) bool { return true }, + KeyboardInteractiveHandler: func(ctx gliderssh.Context, challenge gossh.KeyboardInteractiveChallenge) bool { return true }, + } //exhaustruct:ignore + server.gliderServer.AddHostKey(server.privkey) + + return server, nil +} + +func (server *Server) Run(ctx context.Context) (err error) { + listener, err := misc.Listen(ctx, server.net, server.addr) + if err != nil { + return fmt.Errorf("listen for SSH: %w", err) + } + defer func() { + _ = listener.Close() + }() + + stop := context.AfterFunc(ctx, func() { + shCtx, cancel := context.WithTimeout(context.WithoutCancel(ctx), time.Duration(server.shutdownTimeout)*time.Second) + defer cancel() + _ = server.gliderServer.Shutdown(shCtx) + _ = listener.Close() + }) + defer stop() + + err = server.gliderServer.Serve(listener) + if err != nil { + if errors.Is(err, gliderssh.ErrServerClosed) || ctx.Err() != nil { + return nil + } + return fmt.Errorf("serve SSH: %w", err) + } + panic("unreachable") +} + +func handle(session gliderssh.Session) { + panic("SSH server handler not implemented yet") +} |