Skip to content

Commit

Permalink
Merge pull request #197 from uselagoon/debug-authz
Browse files Browse the repository at this point in the history
Fix handling of persistent SSH connections and improve debug logging
  • Loading branch information
smlx authored Apr 4, 2023
2 parents bacae85 + 570414b commit 3f917b4
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 30 deletions.
4 changes: 4 additions & 0 deletions internal/k8s/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,10 @@ func (c *Client) Exec(ctx context.Context, namespace, deployment,
if err != nil {
return fmt.Errorf("couldn't get executor: %v", err)
}
// Ensure the TerminalSizeQueue goroutine is cancelled immediately after
// command exection completes by deferring its cancellation here.
ctx, cancel := context.WithCancel(ctx)
defer cancel()
// execute the command
return exec.StreamWithContext(ctx, remotecommand.StreamOptions{
Stdin: stdio,
Expand Down
31 changes: 21 additions & 10 deletions internal/sshportalapi/sshportal.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,21 +111,32 @@ func sshportal(ctx context.Context, log *zap.Logger, c *nats.EncodedConn,
zap.Error(err))
return
}
log.Debug("keycloak user attributes",
zap.Strings("realmRoles", realmRoles),
zap.Strings("userGroups", userGroups),
zap.Any("groupProjectIDs", groupProjectIDs),
zap.String("userUUID", user.UUID.String()),
zap.String("sessionID", query.SessionID),
)
// check permission
ok := p.UserCanSSHToEnvironment(ctx, env, realmRoles, userGroups,
groupProjectIDs)
var logMsg string
if ok {
log.Info("validated SSH access",
zap.Int("environmentID", env.ID),
zap.Int("projectID", env.ProjectID),
zap.String("SSHFingerprint", query.SSHFingerprint),
zap.String("environmentName", env.Name),
zap.String("namespace", query.NamespaceName),
zap.String("projectName", env.ProjectName),
zap.String("sessionID", query.SessionID),
zap.String("userUUID", user.UUID.String()),
)
logMsg = "SSH access authorized"
} else {
logMsg = "SSH access not authorized"
}
log.Info(logMsg,
zap.Int("environmentID", env.ID),
zap.Int("projectID", env.ProjectID),
zap.String("SSHFingerprint", query.SSHFingerprint),
zap.String("environmentName", env.Name),
zap.String("namespace", query.NamespaceName),
zap.String("projectName", env.ProjectName),
zap.String("sessionID", query.SessionID),
zap.String("userUUID", user.UUID.String()),
)
if err = c.Publish(replySubject, ok); err != nil {
log.Error("couldn't publish reply",
zap.Any("query", query),
Expand Down
29 changes: 16 additions & 13 deletions internal/sshserver/authhandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,29 +70,32 @@ func pubKeyAuth(log *zap.Logger, nc *nats.EncodedConn,
SessionID: ctx.SessionID(),
}
// send query
var response bool
err = nc.Request(sshportalapi.SubjectSSHAccessQuery, q, &response,
natsTimeout)
var ok bool
err = nc.Request(sshportalapi.SubjectSSHAccessQuery, q, &ok, natsTimeout)
if err != nil {
log.Warn("couldn't make NATS request",
zap.String("sessionID", ctx.SessionID()),
zap.Error(err))
return false
}
// handle response
if response {
authSuccessTotal.Inc()
ctx.SetValue(environmentIDKey, eid)
ctx.SetValue(environmentNameKey, ename)
ctx.SetValue(projectIDKey, pid)
ctx.SetValue(projectNameKey, pname)
ctx.SetValue(sshFingerprint, fingerprint)
log.Debug("Lagoon authorization granted",
if !ok {
log.Debug("SSH access not authorized",
zap.String("sessionID", ctx.SessionID()),
zap.String("fingerprint", fingerprint),
zap.String("namespace", ctx.User()))
return true
return false
}
return false
authSuccessTotal.Inc()
ctx.SetValue(environmentIDKey, eid)
ctx.SetValue(environmentNameKey, ename)
ctx.SetValue(projectIDKey, pid)
ctx.SetValue(projectNameKey, pname)
ctx.SetValue(sshFingerprint, fingerprint)
log.Debug("SSH access authorized",
zap.String("sessionID", ctx.SessionID()),
zap.String("fingerprint", fingerprint),
zap.String("namespace", ctx.User()))
return true
}
}
16 changes: 9 additions & 7 deletions internal/sshserver/sessionhandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ var (
})
)

func sshifyCommand(sftp bool, cmd []string) []string {
// getSSHIntent analyses the SFTP flag and the raw command strings to determine
// if the command should be wrapped.
func getSSHIntent(sftp bool, cmd []string) []string {
// if this is an sftp session we ignore any commands
if sftp {
return []string{"sftp-server", "-u", "0002"}
Expand All @@ -45,16 +47,17 @@ func sshifyCommand(sftp bool, cmd []string) []string {
func sessionHandler(log *zap.Logger, c *k8s.Client, sftp bool) ssh.Handler {
return func(s ssh.Session) {
sessionTotal.Inc()
sid := s.Context().SessionID()
ctx := s.Context()
sid := ctx.SessionID()
// start the command
log.Debug("starting command exec",
zap.String("sessionID", sid),
zap.Strings("rawCommand", s.Command()),
zap.String("subsystem", s.Subsystem()),
)
// parse the command line arguments to extract any service or container args
service, container, cmd := parseConnectionParams(s.Command())
cmd = sshifyCommand(sftp, cmd)
service, container, rawCmd := parseConnectionParams(s.Command())
cmd := getSSHIntent(sftp, rawCmd)
// validate the service and container
if err := k8s.ValidateLabelValue(service); err != nil {
log.Debug("invalid service name",
Expand Down Expand Up @@ -85,7 +88,7 @@ func sessionHandler(log *zap.Logger, c *k8s.Client, sftp bool) ssh.Handler {
return
}
// find the deployment name based on the given service name
deployment, err := c.FindDeployment(s.Context(), s.User(), service)
deployment, err := c.FindDeployment(ctx, s.User(), service)
if err != nil {
log.Debug("couldn't find deployment for service",
zap.String("service", service),
Expand All @@ -103,7 +106,6 @@ func sessionHandler(log *zap.Logger, c *k8s.Client, sftp bool) ssh.Handler {
// check if a pty was requested, and get the window size channel
_, winch, pty := s.Pty()
// extract info passed through the context by the authhandler
ctx := s.Context()
eid, ok := ctx.Value(environmentIDKey).(int)
if !ok {
log.Warn("couldn't extract environment ID from session context")
Expand Down Expand Up @@ -137,7 +139,7 @@ func sessionHandler(log *zap.Logger, c *k8s.Client, sftp bool) ssh.Handler {
zap.String("sessionID", sid),
zap.Strings("command", cmd),
)
err = c.Exec(s.Context(), s.User(), deployment, container, cmd, s,
err = c.Exec(ctx, s.User(), deployment, container, cmd, s,
s.Stderr(), pty, winch)
if err != nil {
if exitErr, ok := err.(exec.ExitError); ok {
Expand Down

0 comments on commit 3f917b4

Please sign in to comment.