From 6da9e99a221c814c50804783c598ec12f78e38d8 Mon Sep 17 00:00:00 2001 From: Runxi Yu Date: Sun, 17 Aug 2025 03:43:08 +0800 Subject: [PATCH] Add some stubs --- forged/internal/common/misc/net.go | 38 ++++++++++++++++++++++++++++++++++++++ forged/internal/common/misc/usock.go | 23 ----------------------- forged/internal/incoming/hooks/hooks.go | 10 +++++----- forged/internal/incoming/lmtp/config.go | 10 +++++----- forged/internal/incoming/ssh/ssh.go | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++- forged/internal/incoming/web/web.go | 57 ++++++++++++++++++++++++++++++++++++++++++++--------- forged/internal/server/server.go | 25 +++++++++++++++++++------ diff --git a/forged/internal/common/misc/net.go b/forged/internal/common/misc/net.go new file mode 100644 index 0000000000000000000000000000000000000000..2c6b0a5a5bb4bc0bccead294c3bff0766beaa8af --- /dev/null +++ b/forged/internal/common/misc/net.go @@ -0,0 +1,38 @@ +package misc + +import ( + "errors" + "fmt" + "net" + "syscall" +) + +func ListenUnixSocket(path string) (listener net.Listener, replaced bool, err error) { + listener, err = net.Listen("unix", path) + if errors.Is(err, syscall.EADDRINUSE) { + replaced = true + if unlinkErr := syscall.Unlink(path); unlinkErr != nil { + return listener, false, fmt.Errorf("remove existing socket %q: %w", path, unlinkErr) + } + listener, err = net.Listen("unix", path) + } + if err != nil { + return listener, replaced, fmt.Errorf("listen on unix socket %q: %w", path, err) + } + return listener, replaced, nil +} + +func Listen(net_, addr string) (listener net.Listener, err error) { + if net_ == "unix" { + listener, _, err = ListenUnixSocket(addr) + if err != nil { + return listener, fmt.Errorf("listen unix socket for web: %w", err) + } + } else { + listener, err = net.Listen(net_, addr) + if err != nil { + return listener, fmt.Errorf("listen %s for web: %w", net_, err) + } + } + return listener, nil +} diff --git a/forged/internal/common/misc/usock.go b/forged/internal/common/misc/usock.go deleted file mode 100644 index 357fa43f751116278541c5c8e53db311d3f106b7..0000000000000000000000000000000000000000 --- a/forged/internal/common/misc/usock.go +++ /dev/null @@ -1,23 +0,0 @@ -package misc - -import ( - "errors" - "fmt" - "net" - "syscall" -) - -func ListenUnixSocket(path string) (listener net.Listener, replaced bool, err error) { - listener, err = net.Listen("unix", path) - if errors.Is(err, syscall.EADDRINUSE) { - replaced = true - if unlinkErr := syscall.Unlink(path); unlinkErr != nil { - return listener, false, fmt.Errorf("remove existing socket %q: %w", path, unlinkErr) - } - listener, err = net.Listen("unix", path) - } - if err != nil { - return listener, replaced, fmt.Errorf("listen on unix socket %q: %w", path, err) - } - return listener, replaced, nil -} diff --git a/forged/internal/incoming/hooks/hooks.go b/forged/internal/incoming/hooks/hooks.go index 64e1cbec1a80d5306107383b3a0e53d964769c3d..18b97426ecfa50ef16c0ce400af3ac0f5dc4cf29 100644 --- a/forged/internal/incoming/hooks/hooks.go +++ b/forged/internal/incoming/hooks/hooks.go @@ -33,15 +33,15 @@ repoName string contribReq string } -func New(config Config) (pool *Server) { +func New(config Config) (server *Server) { return &Server{ socketPath: config.Socket, executablesPath: config.Execs, } } -func (pool *Server) Run() error { - listener, _, err := misc.ListenUnixSocket(pool.socketPath) +func (server *Server) Run() error { + listener, _, err := misc.ListenUnixSocket(server.socketPath) if err != nil { return fmt.Errorf("listen unix socket for hooks: %w", err) } @@ -52,10 +52,10 @@ if err != nil { return fmt.Errorf("accept conn: %w", err) } - go pool.handleConn(conn) + go server.handleConn(conn) } } -func (pool *Server) handleConn(conn net.Conn) { +func (server *Server) handleConn(conn net.Conn) { panic("TODO: handle hook connection") } diff --git a/forged/internal/incoming/lmtp/config.go b/forged/internal/incoming/lmtp/config.go index 767f0e46533fa5f78399fb0c9d288fceb8043d5b..e6db5a693bedff5820c5507ab4fcebc1d463c93b 100644 --- a/forged/internal/incoming/lmtp/config.go +++ b/forged/internal/incoming/lmtp/config.go @@ -23,7 +23,7 @@ WriteTimeout uint32 `scfg:"write_timeout"` ReadTimeout uint32 `scfg:"read_timeout"` } -func New(config Config) (pool *Server) { +func New(config Config) (server *Server) { return &Server{ socket: config.Socket, domain: config.Domain, @@ -33,8 +33,8 @@ readTimeout: config.ReadTimeout, } } -func (pool *Server) Run() error { - listener, _, err := misc.ListenUnixSocket(pool.socket) +func (server *Server) Run() error { + listener, _, err := misc.ListenUnixSocket(server.socket) if err != nil { return fmt.Errorf("listen unix socket for LMTP: %w", err) } @@ -45,10 +45,10 @@ if err != nil { return fmt.Errorf("accept conn: %w", err) } - go pool.handleConn(conn) + go server.handleConn(conn) } } -func (pool *Server) handleConn(conn net.Conn) { +func (server *Server) handleConn(conn net.Conn) { panic("TODO: handle LMTP connection") } diff --git a/forged/internal/incoming/ssh/ssh.go b/forged/internal/incoming/ssh/ssh.go index 74bd99b259acd8dcb85b90e3c46676cb76d98248..b0269e5afa0f15d1211afaad1e0360aa452e932c 100644 --- a/forged/internal/incoming/ssh/ssh.go +++ b/forged/internal/incoming/ssh/ssh.go @@ -1,6 +1,13 @@ package ssh -type Server struct{} +import ( + "fmt" + "os" + + gliderssh "github.com/gliderlabs/ssh" + "go.lindenii.runxiyu.org/forge/forged/internal/common/misc" + gossh "golang.org/x/crypto/ssh" +) type Config struct { Net string `scfg:"net"` @@ -8,3 +15,52 @@ Addr string `scfg:"addr"` Key string `scfg:"key"` Root string `scfg:"root"` } + +type Server struct { + gliderServer *gliderssh.Server + privkey gossh.Signer + pubkeyString string + pubkeyFP string + net string + addr string + root string +} + +func New(config Config) (server *Server, err error) { + server = &Server{ + net: config.Net, + addr: config.Addr, + root: config.Root, + } + + var privkeyBytes []byte + if privkeyBytes, err = os.ReadFile(config.Key); err != nil { + return server, fmt.Errorf("read SSH private key: %w", err) + } + if server.privkey, err = gossh.ParsePrivateKey(privkeyBytes); err != nil { + return server, fmt.Errorf("parse SSH private key: %w", err) + } + server.pubkeyString = misc.BytesToString(gossh.MarshalAuthorizedKey(server.privkey.PublicKey())) + server.pubkeyFP = 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 }, + } + server.gliderServer.AddHostKey(server.privkey) + + return +} + +func (server *Server) Run() (err error) { + listener, err := misc.Listen(server.net, server.addr) + if err = server.gliderServer.Serve(listener); err != nil { + return fmt.Errorf("serve SSH: %w", err) + } + panic("unreachable") +} + +func handle(session gliderssh.Session) { + panic("SSH server handler not implemented yet") +} diff --git a/forged/internal/incoming/web/web.go b/forged/internal/incoming/web/web.go index e338ef28291975ae3d5b5068a8daa54b022264f4..e4675f032af19873c26c29941dd2b55d726a9251 100644 --- a/forged/internal/incoming/web/web.go +++ b/forged/internal/incoming/web/web.go @@ -1,18 +1,57 @@ package web -import "net/http" +import ( + "fmt" + "net/http" + "time" + + "go.lindenii.runxiyu.org/forge/forged/internal/common/misc" +) type Server struct { + net string + addr string + root string httpServer *http.Server } +type handler struct{} + +func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { +} + type Config struct { - Net string `scfg:"net"` - Addr string `scfg:"addr"` - CookieExpiry int `scfg:"cookie_expiry"` - Root string `scfg:"root"` - ReadTimeout uint32 `scfg:"read_timeout"` - WriteTimeout uint32 `scfg:"write_timeout"` - IdleTimeout uint32 `scfg:"idle_timeout"` - ReverseProxy bool `scfg:"reverse_proxy"` + Net string `scfg:"net"` + Addr string `scfg:"addr"` + Root string `scfg:"root"` + CookieExpiry int `scfg:"cookie_expiry"` + ReadTimeout uint32 `scfg:"read_timeout"` + WriteTimeout uint32 `scfg:"write_timeout"` + IdleTimeout uint32 `scfg:"idle_timeout"` + MaxHeaderBytes int `scfg:"max_header_bytes"` + ReverseProxy bool `scfg:"reverse_proxy"` +} + +func New(config Config) (server *Server) { + handler := &handler{} + return &Server{ + net: config.Net, + addr: config.Addr, + root: config.Root, + httpServer: &http.Server{ + Handler: handler, + ReadTimeout: time.Duration(config.ReadTimeout) * time.Second, + WriteTimeout: time.Duration(config.WriteTimeout) * time.Second, + IdleTimeout: time.Duration(config.IdleTimeout) * time.Second, + MaxHeaderBytes: config.MaxHeaderBytes, + }, + } +} + +func (server *Server) Run() (err error) { + listener, err := misc.Listen(server.net, server.addr) + if err = server.httpServer.Serve(listener); err != nil { + return fmt.Errorf("serve web: %w", err) + } + panic("unreachable") } diff --git a/forged/internal/server/server.go b/forged/internal/server/server.go index 1f2cce6342c3a9667d65ccf4309b0f5395a722ba..b3cd92a7b3e02cdc8bf946c1ae7703e9176e6e74 100644 --- a/forged/internal/server/server.go +++ b/forged/internal/server/server.go @@ -37,17 +37,18 @@ if err != nil { return server, fmt.Errorf("open config: %w", err) } - // TODO: Should this belong here, or in Run()? server.database, err = database.Open(ctx, server.config.DB) if err != nil { return server, fmt.Errorf("open database: %w", err) } server.hookServer = hooks.New(server.config.Hooks) - server.lmtpServer = lmtp.New(server.config.LMTP) - - // TODO: Add HTTP and SSH servers + server.webServer = web.New(server.config.Web) + server.sshServer, err = ssh.New(server.config.SSH) + if err != nil { + return server, fmt.Errorf("create SSH server: %w", err) + } return server, nil } @@ -58,13 +59,25 @@ // This needs to be documented somewhere, hence a TODO here for now. go func() { if err := s.hookServer.Run(); err != nil { - log.Fatalf("run hook pool: %v", err) + log.Fatalf("run hook server: %v", err) } }() go func() { if err := s.lmtpServer.Run(); err != nil { - log.Fatalf("run LMTP pool: %v", err) + log.Fatalf("run LMTP server: %v", err) + } + }() + + go func() { + if err := s.webServer.Run(); err != nil { + log.Fatalf("run web server: %v", err) + } + }() + + go func() { + if err := s.sshServer.Run(); err != nil { + log.Fatalf("run SSH server: %v", err) } }() -- 2.48.1