Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Session access controls #1224

Merged
merged 1 commit into from
Aug 26, 2017
Merged

Session access controls #1224

merged 1 commit into from
Aug 26, 2017

Conversation

russjones
Copy link
Contributor

Purpose

As covered in, #1223, this PR addresses two issues. The first is the rule names that gate access to various resources. The second is getting a client to a remote auth server with the logged in users role.

Implementation

For the first issue, the following three rules gate access to sessions.

  • services.KindSSHSession this controls active sessions. This should be hidden from user roles because this is only used by proxies and nodes.
  • services.KindSession this controls recorded sessions. This should be exposed to users and used to gate access to endpoints from the audit log where we fetch session playback related events.
  • services.KindEvent which is used to access the full audit log and emit events to the audit log.

In addition a new endpoint /events/sesssion and function SearchSessionEvents has been created that can only return events related to session start and end which is used to reconstruct the list of completed sessions.

For the second issue, the Dial function for a reversetunnel.RemoteSite has been updated to accept a special string @remote-auth-server which returns a TCP connection to the remote auth server. We then build the authMethods for the logged in user and establish a SSH connection and from there an client to the auth server. This way we get a client with the users role instead of the role of the proxy.

Related Issues

Fixes #1223

@russjones russjones changed the title Session access controls. Session access controls Aug 25, 2017
@russjones russjones force-pushed the rjones/rule-fix branch 2 times, most recently from d97655a to 7e347af Compare August 25, 2017 20:45
@@ -181,6 +181,7 @@ func NewAPIServer(config *APIConfig) http.Handler {
// Audit logs AKA events
srv.POST("/:version/events", srv.withAuth(srv.emitAuditEvent))
srv.GET("/:version/events", srv.withAuth(srv.searchEvents))
srv.GET("/:version/events/session", srv.withAuth(srv.searchSessionEvents)) // return session related events
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comment is redundant

lib/auth/clt.go Outdated
@@ -1227,6 +1227,26 @@ func (c *Client) SearchEvents(from, to time.Time, query string) ([]events.EventF
return retval, nil
}

// SearchSessionEvents returns session relayed events to find completed sessions.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

session-related

@@ -141,6 +141,10 @@ type IAuditLog interface {
// The only mandatory requirement is a date range (UTC). Results must always
// show up sorted by date (newest first)
SearchEvents(fromUTC, toUTC time.Time, query string) ([]EventFields, error)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not simply extend query to add query type for session type events instead of adding extra method?

servers = append(servers, server)
}

log.Infof("got out of band request %v", servers)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debugf

break
}

// log the error if we were not able to connect
log.Error(trace.DebugReport(err))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not an error, connection could fail for various reasons


log.Infof("got out of band request %v", servers)

var connected bool
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

connected flag seems redundant, because conn != nil should do the same

return fn(w, r, p, ctx, site)
})
}

// clientWithUserRole will return an auth.ClientI with the role of the user at
Copy link
Contributor

@klizhentas klizhentas Aug 25, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be a method of SessionContext

}

// remoteClient returns a client to a remote site with the role of logged in user.
func remoteClient(ctx *SessionContext, site reversetunnel.RemoteSite) (auth.ClientI, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

// the requested site. If the site is local a client with the users local role
// is returned. If the site is remote a client with the users remote role is
// returned.
func clientWithUserRole(ctx *SessionContext, site reversetunnel.RemoteSite) (auth.ClientI, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clientWithUserRole -> ctx.GetUserClient()

return client.Dial(network, addr)
}

clt, err := auth.NewClient("http://stub:0", sshDialer)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you will be creating clients on every request. This client should be associated with active user session and should be deleted when the session closes. Extend the logic of the SessionContext to support additional use case.

// remote cluster from our local cluster
agent, err := ctx.GetAgent()
if err != nil {
return nil, trace.Wrap(err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

connection above should be closed if you failed to get agent here, otherwise it will be orphaned resource.

}
principal, authMethods, err := getUserCredentials(agent)
if err != nil {
return nil, trace.Wrap(err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

}
sshConn, sshChans, sshReqs, err := ssh.NewClientConn(netConn, reversetunnel.RemoteAuthServer, config)
if err != nil {
return nil, trace.Wrap(err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

Copy link
Contributor

@klizhentas klizhentas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are several issues to be fixed with this PR especially around resource handling.

}

// look to see if we already have a connection to this cluster
remoteClt, ok := c.remoteClt[site.GetName()]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this could be accessed concurrently, because the session can be used in multiple requests at the same time.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you should protect the access to the client with read write mutex, I suggest to use separate small function with defer for that.

// we'll save the remote client in our session context so we don't have to
// build a new connection next time. all remote clients will be closed when
// the session context is closed.
c.remoteClt[site.GetName()] = rClt
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should call AddCloser here to close client if it supports closing

return nil, trace.Wrap(err)
}

return clt, nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably clt does not support closing directly, but underlying netConn does, so you can return it here with clt explicitly and call AddCloser to close the connection when session ends

@@ -158,6 +265,12 @@ func (c *SessionContext) Close() error {
if c.clt != nil {
return trace.Wrap(c.clt.Close())
}
for _, remoteClt := range c.remoteClt {
err := remoteClt.Close()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, it does, actually, but does it close underlying netConn?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants