From c88ccff5c115dadcdd3804384cbe32f095f8b187 Mon Sep 17 00:00:00 2001 From: Ash McKenzie Date: Tue, 27 Jun 2023 19:11:23 +1000 Subject: [PATCH] WIP: Include metadata in 'access: finish' log line --- cmd/check/main.go | 2 +- .../main.go | 2 +- .../main.go | 2 +- cmd/gitlab-shell/command/command.go | 28 +++++++++++++++++ cmd/gitlab-shell/main.go | 2 +- .../command/authorizedkeys/authorized_keys.go | 9 +++--- .../authorized_principals.go | 7 +++-- internal/command/command.go | 3 +- internal/command/discover/discover.go | 7 +++-- internal/command/healthcheck/healthcheck.go | 9 +++--- .../lfsauthenticate/lfsauthenticate.go | 12 +++---- .../personalaccesstoken.go | 10 +++--- internal/command/receivepack/receivepack.go | 12 +++---- .../twofactorrecover/twofactorrecover.go | 5 +-- .../twofactorverify/twofactorverify.go | 7 +++-- .../command/uploadarchive/uploadarchive.go | 8 ++--- internal/command/uploadpack/uploadpack.go | 10 +++--- internal/sshd/connection.go | 20 ++++++++---- internal/sshd/session.go | 31 +++++++++++-------- internal/sshd/sshd.go | 5 +-- 20 files changed, 121 insertions(+), 70 deletions(-) diff --git a/cmd/check/main.go b/cmd/check/main.go index 578dfdf84..9003bf371 100644 --- a/cmd/check/main.go +++ b/cmd/check/main.go @@ -43,7 +43,7 @@ func main() { ctx, finished := command.Setup(executable.Name, config) defer finished() - if err = cmd.Execute(ctx); err != nil { + if _, err = cmd.Execute(ctx); err != nil { fmt.Fprintf(readWriter.ErrOut, "%v\n", err) os.Exit(1) } diff --git a/cmd/gitlab-shell-authorized-keys-check/main.go b/cmd/gitlab-shell-authorized-keys-check/main.go index e272e68b0..a04be2095 100644 --- a/cmd/gitlab-shell-authorized-keys-check/main.go +++ b/cmd/gitlab-shell-authorized-keys-check/main.go @@ -46,7 +46,7 @@ func main() { ctx, finished := command.Setup(executable.Name, config) defer finished() - if err = cmd.Execute(ctx); err != nil { + if _, err = cmd.Execute(ctx); err != nil { console.DisplayWarningMessage(err.Error(), readWriter.ErrOut) os.Exit(1) } diff --git a/cmd/gitlab-shell-authorized-principals-check/main.go b/cmd/gitlab-shell-authorized-principals-check/main.go index 10d3daab0..999217e4d 100644 --- a/cmd/gitlab-shell-authorized-principals-check/main.go +++ b/cmd/gitlab-shell-authorized-principals-check/main.go @@ -46,7 +46,7 @@ func main() { ctx, finished := command.Setup(executable.Name, config) defer finished() - if err = cmd.Execute(ctx); err != nil { + if _, err = cmd.Execute(ctx); err != nil { console.DisplayWarningMessage(err.Error(), readWriter.ErrOut) os.Exit(1) } diff --git a/cmd/gitlab-shell/command/command.go b/cmd/gitlab-shell/command/command.go index b2a026697..7f93fc152 100644 --- a/cmd/gitlab-shell/command/command.go +++ b/cmd/gitlab-shell/command/command.go @@ -1,6 +1,8 @@ package command import ( + "strings" + "gitlab.com/gitlab-org/gitlab-shell/v14/internal/command" "gitlab.com/gitlab-org/gitlab-shell/v14/internal/command/commandargs" "gitlab.com/gitlab-org/gitlab-shell/v14/internal/command/discover" @@ -17,6 +19,32 @@ import ( "gitlab.com/gitlab-org/gitlab-shell/v14/internal/sshenv" ) +type MetaData struct { + Username string `json:"username"` + Project string `json:"project,omitempty"` + RootNamespace string `json:"root_namespace,omitempty"` +} + +func NewMetaData(project, username string) MetaData { + rootNameSpace := "" + + if len(project) > 0 { + splitFn := func(c rune) bool { + return c == '/' + } + m := strings.FieldsFunc(project, splitFn) + if len(m) > 0 { + rootNameSpace = m[0] + } + } + + return MetaData{ + Username: username, + Project: project, + RootNamespace: rootNameSpace, + } +} + func New(arguments []string, env sshenv.Env, config *config.Config, readWriter *readwriter.ReadWriter) (command.Command, error) { args, err := Parse(arguments, env) if err != nil { diff --git a/cmd/gitlab-shell/main.go b/cmd/gitlab-shell/main.go index b789b774d..679d4593e 100644 --- a/cmd/gitlab-shell/main.go +++ b/cmd/gitlab-shell/main.go @@ -76,7 +76,7 @@ func main() { ctxlog.WithFields(log.Fields{"env": env, "command": cmdName}).Info("gitlab-shell: main: executing command") fips.Check() - if err := cmd.Execute(ctx); err != nil { + if _, err := cmd.Execute(ctx); err != nil { ctxlog.WithError(err).Warn("gitlab-shell: main: command execution failed") if grpcstatus.Convert(err).Code() != grpccodes.Internal { console.DisplayWarningMessage(err.Error(), readWriter.ErrOut) diff --git a/internal/command/authorizedkeys/authorized_keys.go b/internal/command/authorizedkeys/authorized_keys.go index 46ab5c467..438956671 100644 --- a/internal/command/authorizedkeys/authorized_keys.go +++ b/internal/command/authorizedkeys/authorized_keys.go @@ -7,6 +7,7 @@ import ( "gitlab.com/gitlab-org/gitlab-shell/v14/internal/command/commandargs" "gitlab.com/gitlab-org/gitlab-shell/v14/internal/command/readwriter" + "gitlab.com/gitlab-org/gitlab-shell/v14/internal/command/shared/accessverifier" "gitlab.com/gitlab-org/gitlab-shell/v14/internal/config" "gitlab.com/gitlab-org/gitlab-shell/v14/internal/gitlabnet/authorizedkeys" "gitlab.com/gitlab-org/gitlab-shell/v14/internal/keyline" @@ -18,21 +19,21 @@ type Command struct { ReadWriter *readwriter.ReadWriter } -func (c *Command) Execute(ctx context.Context) error { +func (c *Command) Execute(ctx context.Context) (*accessverifier.Response, error) { // Do and return nothing when the expected and actual user don't match. // This can happen when the user in sshd_config doesn't match the user // trying to login. When nothing is printed, the user will be denied access. if c.Args.ExpectedUser != c.Args.ActualUser { // TODO: Log this event once we have a consistent way to log in Go. // See https://gitlab.com/gitlab-org/gitlab-shell/issues/192 for more info. - return nil + return nil, nil } if err := c.printKeyLine(ctx); err != nil { - return err + return nil, err } - return nil + return nil, nil } func (c *Command) printKeyLine(ctx context.Context) error { diff --git a/internal/command/authorizedprincipals/authorized_principals.go b/internal/command/authorizedprincipals/authorized_principals.go index a7cfe1a19..3edceaf59 100644 --- a/internal/command/authorizedprincipals/authorized_principals.go +++ b/internal/command/authorizedprincipals/authorized_principals.go @@ -6,6 +6,7 @@ import ( "gitlab.com/gitlab-org/gitlab-shell/v14/internal/command/commandargs" "gitlab.com/gitlab-org/gitlab-shell/v14/internal/command/readwriter" + "gitlab.com/gitlab-org/gitlab-shell/v14/internal/command/shared/accessverifier" "gitlab.com/gitlab-org/gitlab-shell/v14/internal/config" "gitlab.com/gitlab-org/gitlab-shell/v14/internal/keyline" ) @@ -16,12 +17,12 @@ type Command struct { ReadWriter *readwriter.ReadWriter } -func (c *Command) Execute(ctx context.Context) error { +func (c *Command) Execute(ctx context.Context) (*accessverifier.Response, error) { if err := c.printPrincipalLines(); err != nil { - return err + return nil, err } - return nil + return nil, nil } func (c *Command) printPrincipalLines() error { diff --git a/internal/command/command.go b/internal/command/command.go index d9706b5d0..6f943eacb 100644 --- a/internal/command/command.go +++ b/internal/command/command.go @@ -3,13 +3,14 @@ package command import ( "context" + "gitlab.com/gitlab-org/gitlab-shell/v14/internal/command/shared/accessverifier" "gitlab.com/gitlab-org/gitlab-shell/v14/internal/config" "gitlab.com/gitlab-org/labkit/correlation" "gitlab.com/gitlab-org/labkit/tracing" ) type Command interface { - Execute(ctx context.Context) error + Execute(ctx context.Context) (*accessverifier.Response, error) } // Setup() initializes tracing from the configuration file and generates a diff --git a/internal/command/discover/discover.go b/internal/command/discover/discover.go index 2f81a7844..639fa602c 100644 --- a/internal/command/discover/discover.go +++ b/internal/command/discover/discover.go @@ -6,6 +6,7 @@ import ( "gitlab.com/gitlab-org/gitlab-shell/v14/internal/command/commandargs" "gitlab.com/gitlab-org/gitlab-shell/v14/internal/command/readwriter" + "gitlab.com/gitlab-org/gitlab-shell/v14/internal/command/shared/accessverifier" "gitlab.com/gitlab-org/gitlab-shell/v14/internal/config" "gitlab.com/gitlab-org/gitlab-shell/v14/internal/gitlabnet/discover" ) @@ -16,10 +17,10 @@ type Command struct { ReadWriter *readwriter.ReadWriter } -func (c *Command) Execute(ctx context.Context) error { +func (c *Command) Execute(ctx context.Context) (*accessverifier.Response, error) { response, err := c.getUserInfo(ctx) if err != nil { - return fmt.Errorf("Failed to get username: %v", err) + return nil, fmt.Errorf("Failed to get username: %v", err) } if response.IsAnonymous() { @@ -28,7 +29,7 @@ func (c *Command) Execute(ctx context.Context) error { fmt.Fprintf(c.ReadWriter.Out, "Welcome to GitLab, @%s!\n", response.Username) } - return nil + return nil, nil } func (c *Command) getUserInfo(ctx context.Context) (*discover.Response, error) { diff --git a/internal/command/healthcheck/healthcheck.go b/internal/command/healthcheck/healthcheck.go index e80fe2a1c..ebce03fe3 100644 --- a/internal/command/healthcheck/healthcheck.go +++ b/internal/command/healthcheck/healthcheck.go @@ -5,6 +5,7 @@ import ( "fmt" "gitlab.com/gitlab-org/gitlab-shell/v14/internal/command/readwriter" + "gitlab.com/gitlab-org/gitlab-shell/v14/internal/command/shared/accessverifier" "gitlab.com/gitlab-org/gitlab-shell/v14/internal/config" "gitlab.com/gitlab-org/gitlab-shell/v14/internal/gitlabnet/healthcheck" ) @@ -19,20 +20,20 @@ type Command struct { ReadWriter *readwriter.ReadWriter } -func (c *Command) Execute(ctx context.Context) error { +func (c *Command) Execute(ctx context.Context) (*accessverifier.Response, error) { response, err := c.runCheck(ctx) if err != nil { - return fmt.Errorf("%v: FAILED - %v", apiMessage, err) + return nil, fmt.Errorf("%v: FAILED - %v", apiMessage, err) } fmt.Fprintf(c.ReadWriter.Out, "%v: OK\n", apiMessage) if !response.Redis { - return fmt.Errorf("%v: FAILED", redisMessage) + return nil, fmt.Errorf("%v: FAILED", redisMessage) } fmt.Fprintf(c.ReadWriter.Out, "%v: OK\n", redisMessage) - return nil + return nil, nil } func (c *Command) runCheck(ctx context.Context) (*healthcheck.Response, error) { diff --git a/internal/command/lfsauthenticate/lfsauthenticate.go b/internal/command/lfsauthenticate/lfsauthenticate.go index a06ac93a4..7ee0b6f3b 100644 --- a/internal/command/lfsauthenticate/lfsauthenticate.go +++ b/internal/command/lfsauthenticate/lfsauthenticate.go @@ -37,10 +37,10 @@ type Payload struct { ExpiresIn int `json:"expires_in,omitempty"` } -func (c *Command) Execute(ctx context.Context) error { +func (c *Command) Execute(ctx context.Context) (*accessverifier.Response, error) { args := c.Args.SshArgs if len(args) < 3 { - return disallowedcommand.Error + return nil, disallowedcommand.Error } // e.g. git-lfs-authenticate user/repo.git download @@ -49,12 +49,12 @@ func (c *Command) Execute(ctx context.Context) error { action, err := actionFromOperation(operation) if err != nil { - return err + return nil, err } accessResponse, err := c.verifyAccess(ctx, action, repo) if err != nil { - return err + return nil, err } payload, err := c.authenticate(ctx, operation, repo, accessResponse.UserId) @@ -65,12 +65,12 @@ func (c *Command) Execute(ctx context.Context) error { log.Fields{"operation": operation, "repo": repo, "user_id": accessResponse.UserId}, ).WithError(err).Debug("lfsauthenticate: execute: LFS authentication failed") - return nil + return accessResponse, nil } fmt.Fprintf(c.ReadWriter.Out, "%s\n", payload) - return nil + return accessResponse, nil } func actionFromOperation(operation string) (commandargs.CommandType, error) { diff --git a/internal/command/personalaccesstoken/personalaccesstoken.go b/internal/command/personalaccesstoken/personalaccesstoken.go index fcf7dda1c..a537a7781 100644 --- a/internal/command/personalaccesstoken/personalaccesstoken.go +++ b/internal/command/personalaccesstoken/personalaccesstoken.go @@ -12,6 +12,7 @@ import ( "gitlab.com/gitlab-org/gitlab-shell/v14/internal/command/commandargs" "gitlab.com/gitlab-org/gitlab-shell/v14/internal/command/readwriter" + "gitlab.com/gitlab-org/gitlab-shell/v14/internal/command/shared/accessverifier" "gitlab.com/gitlab-org/gitlab-shell/v14/internal/config" "gitlab.com/gitlab-org/gitlab-shell/v14/internal/gitlabnet/personalaccesstoken" ) @@ -34,10 +35,10 @@ type tokenArgs struct { ExpiresDate string // Calculated, a TTL is passed from command-line. } -func (c *Command) Execute(ctx context.Context) error { +func (c *Command) Execute(ctx context.Context) (*accessverifier.Response, error) { err := c.parseTokenArgs() if err != nil { - return err + return nil, err } log.WithContextFields(ctx, log.Fields{ @@ -46,13 +47,14 @@ func (c *Command) Execute(ctx context.Context) error { response, err := c.getPersonalAccessToken(ctx) if err != nil { - return err + return nil, err } fmt.Fprint(c.ReadWriter.Out, "Token: "+response.Token+"\n") fmt.Fprint(c.ReadWriter.Out, "Scopes: "+strings.Join(response.Scopes, ",")+"\n") fmt.Fprint(c.ReadWriter.Out, "Expires: "+response.ExpiresAt+"\n") - return nil + + return nil, nil } func (c *Command) parseTokenArgs() error { diff --git a/internal/command/receivepack/receivepack.go b/internal/command/receivepack/receivepack.go index c9ef7cdc3..53032e2b8 100644 --- a/internal/command/receivepack/receivepack.go +++ b/internal/command/receivepack/receivepack.go @@ -18,16 +18,16 @@ type Command struct { ReadWriter *readwriter.ReadWriter } -func (c *Command) Execute(ctx context.Context) error { +func (c *Command) Execute(ctx context.Context) (*accessverifier.Response, error) { args := c.Args.SshArgs if len(args) != 2 { - return disallowedcommand.Error + return nil, disallowedcommand.Error } repo := args[1] response, err := c.verifyAccess(ctx, repo) if err != nil { - return err + return nil, err } if response.IsCustomAction() { @@ -42,7 +42,7 @@ func (c *Command) Execute(ctx context.Context) error { Response: response, } - return cmd.Execute(ctx) + return response, cmd.Execute(ctx) } customAction := customaction.Command{ @@ -50,10 +50,10 @@ func (c *Command) Execute(ctx context.Context) error { ReadWriter: c.ReadWriter, EOFSent: true, } - return customAction.Execute(ctx, response) + return response, customAction.Execute(ctx, response) } - return c.performGitalyCall(ctx, response) + return response, c.performGitalyCall(ctx, response) } func (c *Command) verifyAccess(ctx context.Context, repo string) (*accessverifier.Response, error) { diff --git a/internal/command/twofactorrecover/twofactorrecover.go b/internal/command/twofactorrecover/twofactorrecover.go index 7496396ab..b7f610dd9 100644 --- a/internal/command/twofactorrecover/twofactorrecover.go +++ b/internal/command/twofactorrecover/twofactorrecover.go @@ -10,6 +10,7 @@ import ( "gitlab.com/gitlab-org/gitlab-shell/v14/internal/command/commandargs" "gitlab.com/gitlab-org/gitlab-shell/v14/internal/command/readwriter" + "gitlab.com/gitlab-org/gitlab-shell/v14/internal/command/shared/accessverifier" "gitlab.com/gitlab-org/gitlab-shell/v14/internal/config" "gitlab.com/gitlab-org/gitlab-shell/v14/internal/gitlabnet/twofactorrecover" ) @@ -22,7 +23,7 @@ type Command struct { ReadWriter *readwriter.ReadWriter } -func (c *Command) Execute(ctx context.Context) error { +func (c *Command) Execute(ctx context.Context) (*accessverifier.Response, error) { ctxlog := log.ContextLogger(ctx) ctxlog.Debug("twofactorrecover: execute: Waiting for user input") @@ -34,7 +35,7 @@ func (c *Command) Execute(ctx context.Context) error { fmt.Fprintln(c.ReadWriter.Out, "\nNew recovery codes have *not* been generated. Existing codes will remain valid.") } - return nil + return nil, nil } func (c *Command) getUserAnswer(ctx context.Context) string { diff --git a/internal/command/twofactorverify/twofactorverify.go b/internal/command/twofactorverify/twofactorverify.go index 4041de0c8..f25ca3726 100644 --- a/internal/command/twofactorverify/twofactorverify.go +++ b/internal/command/twofactorverify/twofactorverify.go @@ -10,6 +10,7 @@ import ( "gitlab.com/gitlab-org/gitlab-shell/v14/internal/command/commandargs" "gitlab.com/gitlab-org/gitlab-shell/v14/internal/command/readwriter" + "gitlab.com/gitlab-org/gitlab-shell/v14/internal/command/shared/accessverifier" "gitlab.com/gitlab-org/gitlab-shell/v14/internal/config" "gitlab.com/gitlab-org/gitlab-shell/v14/internal/gitlabnet/twofactorverify" ) @@ -25,10 +26,10 @@ type Command struct { ReadWriter *readwriter.ReadWriter } -func (c *Command) Execute(ctx context.Context) error { +func (c *Command) Execute(ctx context.Context) (*accessverifier.Response, error) { client, err := twofactorverify.NewClient(c.Config) if err != nil { - return err + return nil, err } ctx, cancel := context.WithTimeout(ctx, timeout) @@ -67,7 +68,7 @@ func (c *Command) Execute(ctx context.Context) error { log.WithContextFields(ctx, log.Fields{"message": message}).Info("Two factor verify command finished") fmt.Fprintf(c.ReadWriter.Out, "\n%v\n", message) - return nil + return nil, nil } func (c *Command) getOTP(ctx context.Context) (string, error) { diff --git a/internal/command/uploadarchive/uploadarchive.go b/internal/command/uploadarchive/uploadarchive.go index dcdd14407..28fb50d90 100644 --- a/internal/command/uploadarchive/uploadarchive.go +++ b/internal/command/uploadarchive/uploadarchive.go @@ -16,19 +16,19 @@ type Command struct { ReadWriter *readwriter.ReadWriter } -func (c *Command) Execute(ctx context.Context) error { +func (c *Command) Execute(ctx context.Context) (*accessverifier.Response, error) { args := c.Args.SshArgs if len(args) != 2 { - return disallowedcommand.Error + return nil, disallowedcommand.Error } repo := args[1] response, err := c.verifyAccess(ctx, repo) if err != nil { - return err + return nil, err } - return c.performGitalyCall(ctx, response) + return response, c.performGitalyCall(ctx, response) } func (c *Command) verifyAccess(ctx context.Context, repo string) (*accessverifier.Response, error) { diff --git a/internal/command/uploadpack/uploadpack.go b/internal/command/uploadpack/uploadpack.go index 725093a11..c3f93e2e1 100644 --- a/internal/command/uploadpack/uploadpack.go +++ b/internal/command/uploadpack/uploadpack.go @@ -17,16 +17,16 @@ type Command struct { ReadWriter *readwriter.ReadWriter } -func (c *Command) Execute(ctx context.Context) error { +func (c *Command) Execute(ctx context.Context) (*accessverifier.Response, error) { args := c.Args.SshArgs if len(args) != 2 { - return disallowedcommand.Error + return nil, disallowedcommand.Error } repo := args[1] response, err := c.verifyAccess(ctx, repo) if err != nil { - return err + return nil, err } if response.IsCustomAction() { @@ -35,10 +35,10 @@ func (c *Command) Execute(ctx context.Context) error { ReadWriter: c.ReadWriter, EOFSent: false, } - return customAction.Execute(ctx, response) + return response, customAction.Execute(ctx, response) } - return c.performGitalyCall(ctx, response) + return response, c.performGitalyCall(ctx, response) } func (c *Command) verifyAccess(ctx context.Context, repo string) (*accessverifier.Response, error) { diff --git a/internal/sshd/connection.go b/internal/sshd/connection.go index e691d331d..1f37718a3 100644 --- a/internal/sshd/connection.go +++ b/internal/sshd/connection.go @@ -14,6 +14,7 @@ import ( grpcstatus "google.golang.org/grpc/status" "gitlab.com/gitlab-org/gitlab-shell/v14/client" + "gitlab.com/gitlab-org/gitlab-shell/v14/cmd/gitlab-shell/command" "gitlab.com/gitlab-org/gitlab-shell/v14/internal/command/shared/disallowedcommand" "gitlab.com/gitlab-org/gitlab-shell/v14/internal/config" "gitlab.com/gitlab-org/gitlab-shell/v14/internal/metrics" @@ -36,7 +37,7 @@ type connection struct { remoteAddr string } -type channelHandler func(*ssh.ServerConn, ssh.Channel, <-chan *ssh.Request) error +type channelHandler func(*ssh.ServerConn, ssh.Channel, <-chan *ssh.Request) (command.MetaData, error) func newConnection(cfg *config.Config, nconn net.Conn) *connection { maxSessions := cfg.Server.ConcurrentSessionsLimit @@ -50,12 +51,14 @@ func newConnection(cfg *config.Config, nconn net.Conn) *connection { } } -func (c *connection) handle(ctx context.Context, srvCfg *ssh.ServerConfig, handler channelHandler) { +func (c *connection) handle(ctx context.Context, srvCfg *ssh.ServerConfig, handler channelHandler) command.MetaData { + metaData := command.MetaData{} + log.WithContextFields(ctx, log.Fields{}).Info("server: handleConn: start") sconn, chans, err := c.initServerConn(ctx, srvCfg) if err != nil { - return + return metaData } if c.cfg.Server.ClientAliveInterval > 0 { @@ -64,10 +67,12 @@ func (c *connection) handle(ctx context.Context, srvCfg *ssh.ServerConfig, handl go c.sendKeepAliveMsg(ctx, sconn, ticker) } - c.handleRequests(ctx, sconn, chans, handler) + metaData = c.handleRequests(ctx, sconn, chans, handler) reason := sconn.Wait() log.WithContextFields(ctx, log.Fields{"reason": reason}).Info("server: handleConn: done") + + return metaData } func (c *connection) initServerConn(ctx context.Context, srvCfg *ssh.ServerConfig) (*ssh.ServerConn, <-chan ssh.NewChannel, error) { @@ -94,7 +99,8 @@ func (c *connection) initServerConn(ctx context.Context, srvCfg *ssh.ServerConfi return sconn, chans, err } -func (c *connection) handleRequests(ctx context.Context, sconn *ssh.ServerConn, chans <-chan ssh.NewChannel, handler channelHandler) { +func (c *connection) handleRequests(ctx context.Context, sconn *ssh.ServerConn, chans <-chan ssh.NewChannel, handler channelHandler) command.MetaData { + metaData := command.MetaData{} ctxlog := log.WithContextFields(ctx, log.Fields{"remote_addr": c.remoteAddr}) for newChannel := range chans { @@ -134,7 +140,7 @@ func (c *connection) handleRequests(ctx context.Context, sconn *ssh.ServerConn, }() metrics.SliSshdSessionsTotal.Inc() - err := handler(sconn, channel, requests) + metaData, err = handler(sconn, channel, requests) if err != nil { c.trackError(ctxlog, err) } @@ -148,6 +154,8 @@ func (c *connection) handleRequests(ctx context.Context, sconn *ssh.ServerConn, ctx, cancel := context.WithTimeout(ctx, EOFTimeout) defer cancel() c.concurrentSessions.Acquire(ctx, c.maxSessions) + + return metaData } func (c *connection) sendKeepAliveMsg(ctx context.Context, sconn *ssh.ServerConn, ticker *time.Ticker) { diff --git a/internal/sshd/session.go b/internal/sshd/session.go index 3394b2a55..b21c77481 100644 --- a/internal/sshd/session.go +++ b/internal/sshd/session.go @@ -49,7 +49,8 @@ type exitStatusReq struct { ExitStatus uint32 } -func (s *session) handle(ctx context.Context, requests <-chan *ssh.Request) error { +func (s *session) handle(ctx context.Context, requests <-chan *ssh.Request) (shellCmd.MetaData, error) { + metaData := shellCmd.MetaData{} ctxlog := log.ContextLogger(ctx) ctxlog.Debug("session: handle: entering request loop") @@ -70,13 +71,13 @@ func (s *session) handle(ctx context.Context, requests <-chan *ssh.Request) erro case "exec": // The command has been executed as `ssh user@host command` or `exec` channel has been used // in the app implementation - shouldContinue, err = s.handleExec(ctx, req) + metaData, shouldContinue, err = s.handleExec(ctx, req) case "shell": // The command has been entered into the shell or `shell` channel has been used // in the app implementation shouldContinue = false var status uint32 - status, err = s.handleShell(ctx, req) + metaData, status, err = s.handleShell(ctx, req) s.exit(ctx, status) default: // Ignore unknown requests but don't terminate the session @@ -99,7 +100,7 @@ func (s *session) handle(ctx context.Context, requests <-chan *ssh.Request) erro ctxlog.Debug("session: handle: exiting request loop") - return err + return metaData, err } func (s *session) handleEnv(ctx context.Context, req *ssh.Request) (bool, error) { @@ -132,21 +133,24 @@ func (s *session) handleEnv(ctx context.Context, req *ssh.Request) (bool, error) return true, nil } -func (s *session) handleExec(ctx context.Context, req *ssh.Request) (bool, error) { +func (s *session) handleExec(ctx context.Context, req *ssh.Request) (shellCmd.MetaData, bool, error) { + metaData := shellCmd.MetaData{} + var execRequest execRequest if err := ssh.Unmarshal(req.Payload, &execRequest); err != nil { - return false, err + return metaData, false, err } s.execCmd = execRequest.Command - status, err := s.handleShell(ctx, req) + metaData, status, err := s.handleShell(ctx, req) s.exit(ctx, status) - return false, err + return metaData, false, err } -func (s *session) handleShell(ctx context.Context, req *ssh.Request) (uint32, error) { +func (s *session) handleShell(ctx context.Context, req *ssh.Request) (shellCmd.MetaData, uint32, error) { + metaData := shellCmd.MetaData{} ctxlog := log.ContextLogger(ctx) if req.WantReply { @@ -183,7 +187,7 @@ func (s *session) handleShell(ctx context.Context, req *ssh.Request) (uint32, er s.toStderr(ctx, "ERROR: Failed to parse command: %v\n", err.Error()) } - return 128, err + return metaData, 128, err } cmdName := reflect.TypeOf(cmd).String() @@ -194,18 +198,19 @@ func (s *session) handleShell(ctx context.Context, req *ssh.Request) (uint32, er }).Info("session: handleShell: executing command") metrics.SshdSessionEstablishedDuration.Observe(establishSessionDuration) - if err := cmd.Execute(ctx); err != nil { + verifyResponse, err := cmd.Execute(ctx) + if err != nil { grpcStatus := grpcstatus.Convert(err) if grpcStatus.Code() != grpccodes.Internal { s.toStderr(ctx, "ERROR: %v\n", grpcStatus.Message()) } - return 1, err + return metaData, 1, err } ctxlog.Info("session: handleShell: command executed successfully") - return 0, nil + return shellCmd.NewMetaData(verifyResponse.Gitaly.Repo.GlProjectPath, verifyResponse.Username), 0, nil } func (s *session) toStderr(ctx context.Context, format string, args ...interface{}) { diff --git a/internal/sshd/sshd.go b/internal/sshd/sshd.go index f26458228..3461e941a 100644 --- a/internal/sshd/sshd.go +++ b/internal/sshd/sshd.go @@ -13,6 +13,7 @@ import ( "golang.org/x/crypto/ssh" "gitlab.com/gitlab-org/gitlab-shell/v14/client" + shellCmd "gitlab.com/gitlab-org/gitlab-shell/v14/cmd/gitlab-shell/command" "gitlab.com/gitlab-org/gitlab-shell/v14/internal/config" "gitlab.com/gitlab-org/gitlab-shell/v14/internal/gitlabnet" "gitlab.com/gitlab-org/gitlab-shell/v14/internal/metrics" @@ -193,7 +194,7 @@ func (s *Server) handleConn(ctx context.Context, nconn net.Conn) { started := time.Now() conn := newConnection(s.Config, nconn) - conn.handle(ctx, s.serverConfig.get(ctx), func(sconn *ssh.ServerConn, channel ssh.Channel, requests <-chan *ssh.Request) error { + metaData := conn.handle(ctx, s.serverConfig.get(ctx), func(sconn *ssh.ServerConn, channel ssh.Channel, requests <-chan *ssh.Request) (shellCmd.MetaData, error) { session := &session{ cfg: s.Config, channel: channel, @@ -206,7 +207,7 @@ func (s *Server) handleConn(ctx context.Context, nconn net.Conn) { return session.handle(ctx, requests) }) - ctxlog.WithFields(log.Fields{"duration_s": time.Since(started).Seconds()}).Info("access: finish") + ctxlog.WithFields(log.Fields{"duration_s": time.Since(started).Seconds(), "meta": metaData}).Info("access: finish") } func (s *Server) proxyPolicy() (proxyproto.PolicyFunc, error) { -- GitLab