61ad74454c0f6007917976ff4d971b38186abfeb

Author
Ayman Bagabas <ayman.bagabas@gmail.com>
Committer
Ayman Bagabas <ayman.bagabas@gmail.com>
Date

Message

feat(ssh): use custom logging middleware

Diff

  1diff --git a/server/git/errors.go b/server/git/errors.go
  2new file mode 100644
  3index 0000000000000000000000000000000000000000..fa4a8bda254f4920718807f3cecf3989936616a4
  4--- /dev/null
  5+++ b/server/git/errors.go
  6@@ -0,0 +1,23 @@
  7+package git
  8+
  9+import "errors"
 10+
 11+var (
 12+	// ErrNotAuthed represents unauthorized access.
 13+	ErrNotAuthed = errors.New("you are not authorized to do this")
 14+
 15+	// ErrSystemMalfunction represents a general system error returned to clients.
 16+	ErrSystemMalfunction = errors.New("something went wrong")
 17+
 18+	// ErrInvalidRepo represents an attempt to access a non-existent repo.
 19+	ErrInvalidRepo = errors.New("invalid repo")
 20+
 21+	// ErrInvalidRequest represents an invalid request.
 22+	ErrInvalidRequest = errors.New("invalid request")
 23+
 24+	// ErrMaxConnections represents a maximum connection limit being reached.
 25+	ErrMaxConnections = errors.New("too many connections, try again later")
 26+
 27+	// ErrTimeout is returned when the maximum read timeout is exceeded.
 28+	ErrTimeout = errors.New("I/O timeout reached")
 29+)
 30diff --git a/server/git/git.go b/server/git/git.go
 31index e0f1e664ba43dde68c35d652088c89aa1c1dcb03..8baa54a3282226b37e0bdfe8cae988bbf6274873 100644
 32--- a/server/git/git.go
 33+++ b/server/git/git.go
 34@@ -2,7 +2,6 @@ package git
 35 
 36 import (
 37 	"context"
 38-	"errors"
 39 	"fmt"
 40 	"io"
 41 	"path/filepath"
 42@@ -14,27 +13,6 @@ import (
 43 	gitm "github.com/gogs/git-module"
 44 )
 45 
 46-var (
 47-
 48-	// ErrNotAuthed represents unauthorized access.
 49-	ErrNotAuthed = errors.New("you are not authorized to do this")
 50-
 51-	// ErrSystemMalfunction represents a general system error returned to clients.
 52-	ErrSystemMalfunction = errors.New("something went wrong")
 53-
 54-	// ErrInvalidRepo represents an attempt to access a non-existent repo.
 55-	ErrInvalidRepo = errors.New("invalid repo")
 56-
 57-	// ErrInvalidRequest represents an invalid request.
 58-	ErrInvalidRequest = errors.New("invalid request")
 59-
 60-	// ErrMaxConnections represents a maximum connection limit being reached.
 61-	ErrMaxConnections = errors.New("too many connections, try again later")
 62-
 63-	// ErrTimeout is returned when the maximum read timeout is exceeded.
 64-	ErrTimeout = errors.New("I/O timeout reached")
 65-)
 66-
 67 // WritePktline encodes and writes a pktline to the given writer.
 68 func WritePktline(w io.Writer, v ...interface{}) error {
 69 	msg := fmt.Sprintln(v...)
 70diff --git a/server/git/service.go b/server/git/service.go
 71index fb5dbf5acb5340174d178d2fb3c20f38cf4a7098..e0d6877b736c24aeef3754a7ab88c8a5ff8565d7 100644
 72--- a/server/git/service.go
 73+++ b/server/git/service.go
 74@@ -9,7 +9,6 @@ import (
 75 	"os/exec"
 76 	"strings"
 77 
 78-	"github.com/charmbracelet/log"
 79 	"golang.org/x/sync/errgroup"
 80 )
 81 
 82@@ -112,7 +111,6 @@ func gitServiceHandler(ctx context.Context, svc Service, scmd ServiceCommand) er
 83 		}
 84 	}
 85 
 86-	log.Debugf("git service command in %q: %s", cmd.Dir, cmd.String())
 87 	if err := cmd.Start(); err != nil {
 88 		if errors.Is(err, os.ErrNotExist) {
 89 			return ErrInvalidRepo
 90diff --git a/server/ssh/cmd/git.go b/server/ssh/cmd/git.go
 91index d2886514a9b4b7a8d7c9b0fbf752804353dbc5d8..b40b53fa14a0a9b68e49c963b5fb56e12e298652 100644
 92--- a/server/ssh/cmd/git.go
 93+++ b/server/ssh/cmd/git.go
 94@@ -209,16 +209,17 @@ func gitRunE(cmd *cobra.Command, args []string) error {
 95 
 96 	repoPath := filepath.Join(reposDir, repoDir)
 97 	service := git.Service(cmd.Name())
 98+	stdin := cmd.InOrStdin()
 99+	stdout := cmd.OutOrStdout()
100+	stderr := cmd.ErrOrStderr()
101 	scmd := git.ServiceCommand{
102-		Stdin:  cmd.InOrStdin(),
103-		Stdout: s,
104-		Stderr: s.Stderr(),
105+		Stdin:  stdin,
106+		Stdout: stdout,
107+		Stderr: stderr,
108 		Env:    envs,
109 		Dir:    repoPath,
110 	}
111 
112-	logger.Debug("git middleware", "cmd", service, "access", accessLevel.String())
113-
114 	switch service {
115 	case git.ReceivePackService:
116 		receivePackCounter.WithLabelValues(name).Inc()
117@@ -237,16 +238,19 @@ func gitRunE(cmd *cobra.Command, args []string) error {
118 		}
119 
120 		if err := service.Handler(ctx, scmd); err != nil {
121+			logger.Error("failed to handle git service", "service", service, "err", err, "repo", name)
122 			defer func() {
123 				if repo == nil {
124 					// If the repo was created, but the request failed, delete it.
125 					be.DeleteRepository(ctx, name) // nolint: errcheck
126 				}
127 			}()
128+
129 			return git.ErrSystemMalfunction
130 		}
131 
132 		if err := git.EnsureDefaultBranch(ctx, scmd); err != nil {
133+			logger.Error("failed to ensure default branch", "err", err, "repo", name)
134 			return git.ErrSystemMalfunction
135 		}
136 
137@@ -279,7 +283,7 @@ func gitRunE(cmd *cobra.Command, args []string) error {
138 		if errors.Is(err, git.ErrInvalidRepo) {
139 			return git.ErrInvalidRepo
140 		} else if err != nil {
141-			logger.Error("git middleware", "err", err)
142+			logger.Error("failed to handle git service", "service", service, "err", err, "repo", name)
143 			return git.ErrSystemMalfunction
144 		}
145 
146@@ -322,7 +326,7 @@ func gitRunE(cmd *cobra.Command, args []string) error {
147 		}
148 
149 		if err := service.Handler(ctx, scmd); err != nil {
150-			logger.Error("git middleware", "err", err)
151+			logger.Error("failed to handle lfs service", "service", service, "err", err, "repo", name)
152 			return git.ErrSystemMalfunction
153 		}
154 
155diff --git a/server/ssh/middleware.go b/server/ssh/middleware.go
156index 209ed1dd048a275c159125772d5a0a9ca2fea2b0..9a387f49b50f6d3f206a201d6b81905953275c30 100644
157--- a/server/ssh/middleware.go
158+++ b/server/ssh/middleware.go
159@@ -1,6 +1,9 @@
160 package ssh
161 
162 import (
163+	"fmt"
164+	"time"
165+
166 	"github.com/charmbracelet/log"
167 	"github.com/charmbracelet/soft-serve/server/backend"
168 	"github.com/charmbracelet/soft-serve/server/config"
169@@ -49,7 +52,6 @@ func CommandMiddleware(sh ssh.Handler) ssh.Handler {
170 
171 			ctx := s.Context()
172 			cfg := config.FromContext(ctx)
173-			logger := log.FromContext(ctx)
174 
175 			args := s.Command()
176 			cliCommandCounter.WithLabelValues(cmd.CommandName(args)).Inc()
177@@ -110,7 +112,6 @@ func CommandMiddleware(sh ssh.Handler) ssh.Handler {
178 			}
179 
180 			if err := rootCmd.ExecuteContext(ctx); err != nil {
181-				logger.Error("error executing command", "err", err)
182 				s.Exit(1) // nolint: errcheck
183 				return
184 			}
185@@ -118,3 +119,49 @@ func CommandMiddleware(sh ssh.Handler) ssh.Handler {
186 		sh(s)
187 	}
188 }
189+
190+// LoggingMiddleware logs the ssh connection and command.
191+func LoggingMiddleware(sh ssh.Handler) ssh.Handler {
192+	return func(s ssh.Session) {
193+		ctx := s.Context()
194+		logger := log.FromContext(ctx).WithPrefix("ssh")
195+		ct := time.Now()
196+		hpk := sshutils.MarshalAuthorizedKey(s.PublicKey())
197+		ptyReq, _, isPty := s.Pty()
198+		addr := s.RemoteAddr().String()
199+		user := proto.UserFromContext(ctx)
200+		logArgs := []interface{}{
201+			"addr",
202+			addr,
203+			"cmd",
204+			s.Command(),
205+		}
206+
207+		if user != nil {
208+			logArgs = append([]interface{}{
209+				"username",
210+				user.Username(),
211+			}, logArgs...)
212+		}
213+
214+		if isPty {
215+			logArgs = []interface{}{
216+				"term", ptyReq.Term,
217+				"width", ptyReq.Window.Width,
218+				"height", ptyReq.Window.Height,
219+			}
220+		}
221+
222+		if config.IsVerbose() {
223+			logArgs = append(logArgs,
224+				"key", hpk,
225+				"envs", s.Environ(),
226+			)
227+		}
228+
229+		msg := fmt.Sprintf("user %q", s.User())
230+		logger.Debug(msg+" connected", logArgs...)
231+		sh(s)
232+		logger.Debug(msg+" disconnected", append(logArgs, "duration", time.Since(ct))...)
233+	}
234+}
235diff --git a/server/ssh/ssh.go b/server/ssh/ssh.go
236index bd4fd4a899d2831886050a043056ba9779be2ae1..d02824da59c88a2383639e037db278f9c78f114b 100644
237--- a/server/ssh/ssh.go
238+++ b/server/ssh/ssh.go
239@@ -18,7 +18,6 @@ import (
240 	"github.com/charmbracelet/ssh"
241 	"github.com/charmbracelet/wish"
242 	bm "github.com/charmbracelet/wish/bubbletea"
243-	lm "github.com/charmbracelet/wish/logging"
244 	rm "github.com/charmbracelet/wish/recover"
245 	"github.com/muesli/termenv"
246 	"github.com/prometheus/client_golang/prometheus"
247@@ -74,12 +73,10 @@ func NewSSHServer(ctx context.Context) (*SSHServer, error) {
248 			bm.MiddlewareWithProgramHandler(SessionHandler, termenv.ANSI256),
249 			// CLI middleware.
250 			CommandMiddleware,
251+			// Logging middleware.
252+			LoggingMiddleware,
253 			// Context middleware.
254 			ContextMiddleware(cfg, dbx, datastore, be, logger),
255-			// Logging middleware.
256-			lm.MiddlewareWithLogger(
257-				&loggerAdapter{logger, log.DebugLevel},
258-			),
259 		),
260 	}
261 
262diff --git a/server/web/git.go b/server/web/git.go
263index 121b85795d2e4a0dfa7aa2dafd6f6e0c8990b5ea..4d4dde8b2f4df382eac9b7328cdd92af7bb34f95 100644
264--- a/server/web/git.go
265+++ b/server/web/git.go
266@@ -75,7 +75,6 @@ var (
267 func withParams(h http.Handler) http.Handler {
268 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
269 		ctx := r.Context()
270-		logger := log.FromContext(ctx)
271 		cfg := config.FromContext(ctx)
272 		vars := mux.Vars(r)
273 		repo := vars["repo"]