diff --git a/cmd/check/main.go b/cmd/check/main.go index 578dfdf8434a716be197761cff8aafbc09da1d1a..9003bf3716cd29a8513ff38cef657a46ba244ec1 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 e272e68b0a8635a6e57cde621d4cbfa2180d629e..a04be2095c1a4b444666bc69bc6cd53a1c4c87c7 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 10d3daab09f8b2bfc1836068a31fb92ced8a02fb..999217e4d03d3ec3aee3e9563d5fbbeab631f8fb 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 b2a0266972c09f505eb3d24800102bbe07f727a7..7f93fc1526b0245013e52040d09860c926fda70c 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 b789b774d89ed9d8fd7b62fb42c2cfe6b5c650a9..679d4593ee8118a602d4cdcca81bde4999fff14a 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 46ab5c467f98fdd5a265eb8e056833cefa13ca19..43895667157be88e85710bd442cc91c3d697e74b 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 a7cfe1a192a2184845e37c1e4e623901d8a83c80..3edceaf598473f6dfaeb4c15d06b3917445feceb 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 d9706b5d05a76b9cb6f18732d20137b9c5f97774..6f943eacb1f85fae3e91efa265accc47125fda2c 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 2f81a78445820badaa2ac4a86aebf616cd5fdea2..639fa602c1a273eb154be3c045c14adaddce1ea3 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 e80fe2a1c6df22d047367d7191158efdf5bff0a5..ebce03fe338aa6a2d0d928cba13bb62e2373521e 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 a06ac93a42af87931b620a373ba00c9cfd70a032..7ee0b6f3b4f8fe74ae5f76fdaf5ed6656210afd1 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 fcf7dda1c8d066ea4b4af6ca77bbcba164ab5069..a537a77819026c487a54c047684f50aef2cc64d7 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 c9ef7cdc386163cd4a347caa058a5d5bd57444f0..53032e2b8a42667006e78dc19f817f2e008f54bc 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 7496396ab2dc96e7b00191929fa24aa2caf74c46..b7f610dd9661b6e76e8c6000606db69fb155506f 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 4041de0c855754deef662e0143a644e9ad091a24..f25ca3726b6001b1a9522dfd34ed1a713ccaca93 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 dcdd144076dd9ccf5ad90342aabacf3373de76ed..28fb50d904be777edf3c79b7f576c24a80c9d29f 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 725093a11bb9b919e1e6fb3fc3bd211aa937180d..c3f93e2e16d9cf09fc52ca97b9dbb47de0fd4943 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 e691d331d46ff82143d198918e123b9d8246d52a..1f37718a3d817d8c950e28ba4bf72bc276d889a8 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 3394b2a55fd31d919f9db064f09955b25d6aa59b..b21c77481a52e565bc21b970830ca80258971d1d 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 f26458228eee787cdf500ed4c3ad62a8c5e22da8..3461e941ad19c866fec18493986bf55791cefc19 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) {