From 46a3b22e557fd2fcb545a721682995319d355f94 Mon Sep 17 00:00:00 2001 From: Ayman Bagabas Date: Wed, 7 Dec 2022 13:55:52 -0500 Subject: [PATCH] feat(server): disable git daemon --- server/server.go | 42 +++++++++++++---------- server/server_test.go | 75 +++++++++++++++++++++++++----------------- server/session_test.go | 32 +++++++++++------- 3 files changed, 91 insertions(+), 58 deletions(-) diff --git a/server/server.go b/server/server.go index d17d017bde9f4b6d4cfc7db134a2d744ed005c9a..b4e114bf32cc7af039e842de714d2a91a6e74175 100644 --- a/server/server.go +++ b/server/server.go @@ -75,11 +75,13 @@ func NewServer(cfg *config.Config) *Server { sh.IdleTimeout = time.Duration(cfg.SSH.IdleTimeout) * time.Second } s.SSHServer = sh - d, err := daemon.NewDaemon(cfg) - if err != nil { - log.Fatalln(err) + if cfg.Git.Enabled { + d, err := daemon.NewDaemon(cfg) + if err != nil { + log.Fatalln(err) + } + s.GitServer = d } - s.GitServer = d return s } @@ -92,13 +94,15 @@ func (s *Server) Reload() error { // Start starts the SSH server. func (s *Server) Start() error { var errg errgroup.Group - errg.Go(func() error { - log.Printf("Starting Git server on %s:%d", s.Config.Host, s.Config.Git.Port) - if err := s.GitServer.Start(); err != daemon.ErrServerClosed { - return err - } - return nil - }) + if s.Config.Git.Enabled { + errg.Go(func() error { + log.Printf("Starting Git server on %s:%d", s.Config.Host, s.Config.Git.Port) + if err := s.GitServer.Start(); err != daemon.ErrServerClosed { + return err + } + return nil + }) + } errg.Go(func() error { log.Printf("Starting SSH server on %s:%d", s.Config.Host, s.Config.SSH.Port) if err := s.SSHServer.ListenAndServe(); err != ssh.ErrServerClosed { @@ -112,12 +116,14 @@ func (s *Server) Start() error { // Shutdown lets the server gracefully shutdown. func (s *Server) Shutdown(ctx context.Context) error { var errg errgroup.Group + if s.Config.Git.Enabled { + errg.Go(func() error { + return s.GitServer.Shutdown(ctx) + }) + } errg.Go(func() error { return s.SSHServer.Shutdown(ctx) }) - errg.Go(func() error { - return s.GitServer.Shutdown(ctx) - }) return errg.Wait() } @@ -127,8 +133,10 @@ func (s *Server) Close() error { errg.Go(func() error { return s.SSHServer.Close() }) - errg.Go(func() error { - return s.GitServer.Close() - }) + if s.Config.Git.Enabled { + errg.Go(func() error { + return s.GitServer.Close() + }) + } return errg.Wait() } diff --git a/server/server_test.go b/server/server_test.go index 064d5ae296bac38a4e00fa95bed28c3c52d8e827..9f570abb3db737914816a289da86cc388091ba53 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -2,35 +2,29 @@ package server import ( "fmt" - "log" "net" "os" "path/filepath" + "strconv" + "strings" "testing" "github.com/charmbracelet/keygen" - ggit "github.com/charmbracelet/soft-serve/git" "github.com/charmbracelet/soft-serve/server/config" "github.com/gliderlabs/ssh" + "github.com/go-git/go-billy/v5/memfs" "github.com/go-git/go-git/v5" gconfig "github.com/go-git/go-git/v5/config" "github.com/go-git/go-git/v5/plumbing/object" gssh "github.com/go-git/go-git/v5/plumbing/transport/ssh" + "github.com/go-git/go-git/v5/storage/memory" "github.com/matryer/is" - cssh "golang.org/x/crypto/ssh" -) - -var ( - cfg = &config.Config{ - Host: "", - Git: config.GitConfig{Port: 9418}, - } + gossh "golang.org/x/crypto/ssh" ) func TestPushRepo(t *testing.T) { is := is.New(t) - _, pkPath := createKeyPair(t) - s := setupServer(t) + s, cfg, pkPath := setupServer(t) err := s.Reload() is.NoErr(err) rp := t.TempDir() @@ -59,8 +53,9 @@ func TestPushRepo(t *testing.T) { auth, err := gssh.NewPublicKeysFromFile("git", pkPath, "") is.NoErr(err) auth.HostKeyCallbackHelper = gssh.HostKeyCallbackHelper{ - HostKeyCallback: cssh.InsecureIgnoreHostKey(), + HostKeyCallback: gossh.InsecureIgnoreHostKey(), } + t.Logf("pushing to ssh://localhost:%d/%s", cfg.SSH.Port, "testrepo") err = r.Push(&git.PushOptions{ RemoteName: "origin", Auth: auth, @@ -70,21 +65,23 @@ func TestPushRepo(t *testing.T) { func TestCloneRepo(t *testing.T) { is := is.New(t) - _, pkPath := createKeyPair(t) - s := setupServer(t) - log.Print("starting server") + s, cfg, pkPath := setupServer(t) + t.Log("starting server") err := s.Reload() - log.Print("reloaded server") + t.Log("reloaded server") is.NoErr(err) dst := t.TempDir() + t.Cleanup(func() { is.NoErr(os.RemoveAll(dst)) }) url := fmt.Sprintf("ssh://localhost:%d/config", cfg.SSH.Port) - log.Print("cloning repo") - err = ggit.Clone(url, dst, ggit.CloneOptions{ - CommandOptions: ggit.CommandOptions{ - Envs: []string{ - fmt.Sprintf(`GIT_SSH_COMMAND=ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i %s -F /dev/null`, pkPath), - }, - }, + t.Log("cloning repo") + pk, err := gssh.NewPublicKeysFromFile("git", pkPath, "") + is.NoErr(err) + pk.HostKeyCallbackHelper = gssh.HostKeyCallbackHelper{ + HostKeyCallback: gossh.InsecureIgnoreHostKey(), + } + _, err = git.Clone(memory.NewStorage(), memfs.New(), &git.CloneOptions{ + URL: url, + Auth: pk, }) is.NoErr(err) } @@ -95,20 +92,34 @@ func randomPort() int { return addr.Addr().(*net.TCPAddr).Port } -func setupServer(t *testing.T) *Server { +func setupServer(t *testing.T) (*Server, *config.Config, string) { t.Helper() - cfg.DataPath = t.TempDir() - cfg.SSH.Port = randomPort() + is := is.New(t) + pub, pkPath := createKeyPair(t) + dp := t.TempDir() + is.NoErr(os.Setenv("SOFT_SERVE_DATA_PATH", dp)) + is.NoErr(os.Setenv("SOFT_SERVE_INITIAL_ADMIN_KEY", authorizedKey(pub))) + is.NoErr(os.Setenv("SOFT_SERVE_GIT_ENABLED", "false")) + is.NoErr(os.Setenv("SOFT_SERVE_SSH_PORT", strconv.Itoa(randomPort()))) + // is.NoErr(os.Setenv("SOFT_SERVE_DB_DRIVER", "fake")) + t.Cleanup(func() { + is.NoErr(os.Unsetenv("SOFT_SERVE_DATA_PATH")) + is.NoErr(os.Unsetenv("SOFT_SERVE_SSH_PORT")) + is.NoErr(os.Unsetenv("SOFT_SERVE_INITIAL_ADMIN_KEY")) + is.NoErr(os.Unsetenv("SOFT_SERVE_GIT_ENABLED")) + // is.NoErr(os.Unsetenv("SOFT_SERVE_DB_DRIVER")) + is.NoErr(os.RemoveAll(dp)) + }) + cfg := config.DefaultConfig() //.WithDB(&fakedb.FakeDB{}) s := NewServer(cfg) go func() { - log.Print("starting server") + t.Log("starting server") s.Start() }() t.Cleanup(func() { s.Close() - os.RemoveAll(cfg.DataPath) }) - return s + return s, cfg, pkPath } func createKeyPair(t *testing.T) (ssh.PublicKey, string) { @@ -121,3 +132,7 @@ func createKeyPair(t *testing.T) (ssh.PublicKey, string) { is.NoErr(err) return pubkey, filepath.Join(keyDir, "id_ed25519") } + +func authorizedKey(pk ssh.PublicKey) string { + return strings.TrimSpace(string(gossh.MarshalAuthorizedKey(pk))) +} diff --git a/server/session_test.go b/server/session_test.go index d4b97a0b930893459ff6a3eaf99ac22c3a1ab8a3..0876167cd56dbf0248cd0a5e9d6692d13d532c64 100644 --- a/server/session_test.go +++ b/server/session_test.go @@ -4,11 +4,11 @@ import ( "bytes" "errors" "os" + "strconv" "strings" "testing" "time" - "github.com/charmbracelet/soft-serve/proto" cm "github.com/charmbracelet/soft-serve/server/cmd" "github.com/charmbracelet/soft-serve/server/config" bm "github.com/charmbracelet/wish/bubbletea" @@ -28,6 +28,12 @@ func TestSession(t *testing.T) { defer s.Close() err := s.RequestPty("xterm", 80, 40, nil) is.NoErr(err) + go func() { + time.Sleep(1 * time.Second) + s.Signal(gossh.SIGTERM) + // FIXME: exit with code 0 instead of forcibly closing the session + s.Close() + }() err = s.Run("config") // Session writes error and exits is.True(strings.Contains(out.String(), cm.ErrUnauthorized.Error())) @@ -54,16 +60,20 @@ func TestSession(t *testing.T) { func setup(tb testing.TB) *gossh.Session { tb.Helper() - cfg := &config.Config{ - Host: "", - SSH: config.SSHConfig{Port: randomPort()}, - Git: config.GitConfig{Port: 9418}, - DataPath: tb.TempDir(), - InitialAdminKeys: []string{ - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMJlb/qf2B2kMNdBxfpCQqI2ctPcsOkdZGVh5zTRhKtH", - }, - AnonAccess: proto.ReadOnlyAccess, - } + is := is.New(tb) + dp := tb.TempDir() + is.NoErr(os.Setenv("SOFT_SERVE_DATA_PATH", dp)) + is.NoErr(os.Setenv("SOFT_SERVE_ANON_ACCESS", "read-only")) + is.NoErr(os.Setenv("SOFT_SERVE_GIT_PORT", "9418")) + is.NoErr(os.Setenv("SOFT_SERVE_SSH_PORT", strconv.Itoa(randomPort()))) + tb.Cleanup(func() { + is.NoErr(os.Unsetenv("SOFT_SERVE_DATA_PATH")) + is.NoErr(os.Unsetenv("SOFT_SERVE_ANON_ACCESS")) + is.NoErr(os.Unsetenv("SOFT_SERVE_GIT_PORT")) + is.NoErr(os.Unsetenv("SOFT_SERVE_SSH_PORT")) + is.NoErr(os.RemoveAll(dp)) + }) + cfg := config.DefaultConfig() return testsession.New(tb, &ssh.Server{ Handler: bm.MiddlewareWithProgramHandler(SessionHandler(cfg), termenv.ANSI256)(func(s ssh.Session) { _, _, active := s.Pty()