Skip to content
New issue

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

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

Already on GitHub? # to your account

Sasha/rbac #652

Merged
merged 47 commits into from
Dec 23, 2016
Merged

Sasha/rbac #652

merged 47 commits into from
Dec 23, 2016

Conversation

klizhentas
Copy link
Contributor

RBAC: Part 1

This PR adds core RBAC set for #620 , does not address external OIDC users yet.

I haven't vendored things either yet to simplify code reivew.

@klizhentas klizhentas added this to the 1.5 milestone Dec 21, 2016
@klizhentas klizhentas requested a review from kontsevoy December 21, 2016 23:10

clt, err := NewClient(s.srv.URL, nil)
clt, err := NewClient(s.srv.URL, nil, roundtrip.BasicAuth(teleport.RoleAdmin.User(), "<something>"))
Copy link
Contributor

Choose a reason for hiding this comment

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

need a comment explaining what is this for. I kind of understand the purpose, but not really. IIRC the security on cluster API (aka site API) is done on the SSH level, HTTP does not matter, so why basic auth?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

this is just for tests to fake auth in some cases, I will add comment

Copy link
Contributor Author

Choose a reason for hiding this comment

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

✅ applied

role := services.RoleForUser(teleportUser)
err := s.a.UpsertRole(role)
c.Assert(err, IsNil)
teleportUser.Roles = []string{role.GetMetadata().Name}
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 so frequent that adding role.GetName() would make sense

Copy link
Contributor Author

Choose a reason for hiding this comment

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

✅ applied

return trace.AccessDenied("user %v is locked until %v", utils.HumanTimeFormat(status.LockExpires))
}
fnErr := fn()
if !trace.IsAccessDenied(fnErr) && !trace.IsBadParameter(fnErr) {
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 dangerous. if fn() does not return a properly wrapped trace error object (which will happen), "access denied" behavior will break.

access denied situation needs to be bulletproof, maybe like error, accessGranted := fun(). where accessGranted is a boolean.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

✅ good catch. I've made it more restrictive.

return s.Authority.GenerateUserCert(privateKey, key, username, allowedLogins, ttl)
}

func (s *AuthServer) withUserLock(username string, fn func() error) error {
Copy link
Contributor

Choose a reason for hiding this comment

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

This function seems to be pretty critical to security, I would provide detailed comments on its purpose and expectations for fn().

Copy link
Contributor Author

Choose a reason for hiding this comment

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

✅ applied

@@ -614,6 +677,13 @@ func (s *AuthServer) GetWebSessionInfo(userName string, id string) (*Session, er
}, nil
}

func (s *AuthServer) DeleteNamespace(namespace string) error {
if namespace == defaults.Namespace {
return trace.AccessDenied("can't delete default namespace")
Copy link
Contributor

Choose a reason for hiding this comment

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

the default namespace: %s would be better

Copy link
Contributor

Choose a reason for hiding this comment

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

or... is the default an empty string?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

no, default is literally default

@@ -140,7 +140,7 @@ func (s *AuthServer) Create#U2FRegisterRequest(token string) (u2fRegisterRe
return nil, trace.Wrap(err)
}

lock := "#token"+token
lock := "#token" + token
Copy link
Contributor

Choose a reason for hiding this comment

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

haha, we're running different versions of gofmt - everyone should upgrade.

@@ -216,6 +216,13 @@ func (s *AuthServer) CreateUserWithToken(token, password, hotpToken string) (*Se
}

// apply user allowed logins
role := services.RoleForUser(&tokenData.User)
if err := s.UpsertRole(role); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

this will always auto-create "user:user" role. I am not sure it's a desired behavior especially when I'm calling tctl user add --role=restricted ev

Copy link
Contributor Author

Choose a reason for hiding this comment

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

✅ fixed will only create user once

}
} else {
if err := a.Access.DeleteRole(role.GetMetadata().Name); err != nil {
return 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.

"not found" error should be ignored in this case, because (see my comments above) in a role-based environment, I (as a teleport admin) would want to delete these auto-generated roles, almost always.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@@ -525,7 +532,7 @@ func (tc *TeleportClient) Join(sessionID session.ID, input io.Reader) (err error
// connect to server:
fullNodeAddr := node.Addr
if tc.SiteName != "" {
fullNodeAddr = fmt.Sprintf("%s@%s", node.Addr, tc.SiteName)
fullNodeAddr = fmt.Sprintf("%s@%s@%s", node.Addr, tc.Namespace, tc.SiteName)
Copy link
Contributor

Choose a reason for hiding this comment

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

wow. this breaks compatibility between clients/servers and why is it even necessary? the server already knows the namespace of a node and will reject access based on RBAC rules.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

old format is still supported. I will double check with old clients of course

@@ -94,6 +94,9 @@ type Config struct {
// Labels represent host Labels
Labels map[string]string

// Namespace is nodes namespace
Namespace string
Copy link
Contributor

Choose a reason for hiding this comment

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

I do not think namespaces should be exposed to clients at all.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

talked about this in person - decided to keep it as is as experimental feature for 1.4. release

@klizhentas klizhentas merged commit be5cc56 into master Dec 23, 2016
@klizhentas klizhentas deleted the sasha/rbac branch December 23, 2016 17:56
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants