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

Adds support for kube_users, extend interpolation #3404

Merged
merged 1 commit into from
Mar 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,10 @@ const (
// allowed kubernetes groups
TraitKubeGroups = "kubernetes_groups"

// TraitKubeUsers is the name the role variable used to store
// allowed kubernetes users
TraitKubeUsers = "kubernetes_users"

// TraitInternalLoginsVariable is the variable used to store allowed
// logins for local accounts.
TraitInternalLoginsVariable = "{{internal.logins}}"
Expand Down
2 changes: 1 addition & 1 deletion e
Submodule e updated from 821f9d to 301530
85 changes: 83 additions & 2 deletions integration/kube_integration_test.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Gravitational, Inc.
Copyright 2016-2020 Gravitational, Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -155,6 +155,7 @@ func (s *KubeSuite) TestKubeExec(c *check.C) {
Allow: services.RoleConditions{
Logins: []string{username},
KubeGroups: []string{teleport.KubeSystemMasters},
KubeUsers: []string{"alice@example.com"},
},
})
t.AddUserWithRole(username, role)
Expand All @@ -166,7 +167,8 @@ func (s *KubeSuite) TestKubeExec(c *check.C) {
c.Assert(err, check.IsNil)
defer t.Stop(true)

// impersonating client requests will be denied
// impersonating client requests will be denied if the headers
// are referencing users or groups not allowed by the existing roles
impersonatingProxyClient, impersonatingProxyClientConfig, err := kubeProxyClient(kubeProxyConfig{
t: t,
username: username,
Expand All @@ -180,6 +182,23 @@ func (s *KubeSuite) TestKubeExec(c *check.C) {
})
c.Assert(err, check.NotNil)

// scoped client requests will be allowed, as long as the impersonation headers
// are referencing users and groups allowed by existing roles
scopedProxyClient, scopedProxyClientConfig, err := kubeProxyClient(kubeProxyConfig{
t: t,
username: username,
impersonation: &rest.ImpersonationConfig{
UserName: role.GetKubeUsers(services.Allow)[0],
Groups: role.GetKubeGroups(services.Allow),
},
})
c.Assert(err, check.IsNil)

_, err = scopedProxyClient.Core().Pods(kubeSystemNamespace).List(metav1.ListOptions{
LabelSelector: kubeDNSLabels.AsSelector().String(),
})
c.Assert(err, check.IsNil)

// set up kube configuration using proxy
proxyClient, proxyClientConfig, err := kubeProxyClient(kubeProxyConfig{t: t, username: username})
c.Assert(err, check.IsNil)
Expand Down Expand Up @@ -266,6 +285,68 @@ loop:
})
c.Assert(err, check.NotNil)
c.Assert(err.Error(), check.Matches, ".*impersonation request has been denied.*")

// scoped kube exec is allowed, impersonation headers
// are allowed by the role
term = NewTerminal(250)
term.Type("\aecho hi\n\r\aexit\n\r\a")
out = &bytes.Buffer{}
err = kubeExec(scopedProxyClientConfig, kubeExecArgs{
podName: pod.Name,
podNamespace: pod.Namespace,
container: kubeDNSContainer,
command: []string{"/bin/sh"},
stdout: out,
tty: true,
stdin: &term,
})
c.Assert(err, check.IsNil)
}

// TestKubeDeny makes sure that deny rule conflicting with allow
// rule takes precendence
func (s *KubeSuite) TestKubeDeny(c *check.C) {
tconf := s.teleKubeConfig(Host)

t := NewInstance(InstanceConfig{
ClusterName: Site,
HostID: HostID,
NodeName: Host,
Ports: s.ports.PopIntSlice(5),
Priv: s.priv,
Pub: s.pub,
})

username := s.me.Username
role, err := services.NewRole("kubemaster", services.RoleSpecV3{
Allow: services.RoleConditions{
Logins: []string{username},
KubeGroups: []string{teleport.KubeSystemMasters},
KubeUsers: []string{"alice@example.com"},
},
Deny: services.RoleConditions{
KubeGroups: []string{teleport.KubeSystemMasters},
KubeUsers: []string{"alice@example.com"},
},
})
t.AddUserWithRole(username, role)

err = t.CreateEx(nil, tconf)
c.Assert(err, check.IsNil)

err = t.Start()
c.Assert(err, check.IsNil)
defer t.Stop(true)

// set up kube configuration using proxy
proxyClient, _, err := kubeProxyClient(kubeProxyConfig{t: t, username: username})
c.Assert(err, check.IsNil)

// try get request to fetch available pods
_, err = proxyClient.Core().Pods(kubeSystemNamespace).List(metav1.ListOptions{
LabelSelector: kubeDNSLabels.AsSelector().String(),
})
c.Assert(err, check.NotNil)
}

// TestKubePortForward tests kubernetes port forwarding
Expand Down
11 changes: 7 additions & 4 deletions lib/auth/github.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright 2017 Gravitational, Inc.
Copyright 2017-2020 Gravitational, Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -289,9 +289,12 @@ type createUserParams struct {
// logins is the list of *nix logins.
logins []string

// kubeGroups is the list of Kubernetes this user belongs to.
// kubeGroups is the list of Kubernetes groups this user belongs to.
kubeGroups []string

// kubeUsers is the list of Kubernetes users this user belongs to.
kubeUsers []string

// roles is the list of roles this user is assigned to.
roles []string

Expand All @@ -309,14 +312,14 @@ func (s *AuthServer) calculateGithubUser(connector services.GithubConnector, cla
}

// Calculate logins, kubegroups, roles, and traits.
p.logins, p.kubeGroups = connector.MapClaims(*claims)
p.logins, p.kubeGroups, p.kubeUsers = connector.MapClaims(*claims)
if len(p.logins) == 0 {
return nil, trace.BadParameter(
"user %q does not belong to any teams configured in %q connector",
claims.Username, connector.GetName())
}
p.roles = modules.GetModules().RolesFromLogins(p.logins)
p.traits = modules.GetModules().TraitsFromLogins(p.logins, p.kubeGroups)
p.traits = modules.GetModules().TraitsFromLogins(p.logins, p.kubeGroups, p.kubeUsers)

// Pick smaller for role: session TTL from role or requested TTL.
roles, err := services.FetchRoles(p.roles, s.Access, p.traits)
Expand Down
3 changes: 2 additions & 1 deletion lib/auth/kube.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ func (s *AuthServer) ProcessKubeCSR(req KubeCSR) (*KubeCSRResponse, error) {

// extract and encode the kubernetes groups of the authenticated
// user in the newly issued certificate
kubernetesGroups, err := roles.CheckKubeGroups(0)
kubernetesGroups, kubernetesUsers, err := roles.CheckKubeGroupsAndUsers(0)
if err != nil {
return nil, trace.Wrap(err)
}
Expand Down Expand Up @@ -129,6 +129,7 @@ func (s *AuthServer) ProcessKubeCSR(req KubeCSR) (*KubeCSRResponse, error) {
// otherwise proxies can generate certs for any user.
Usage: []string{teleport.UsageKubeOnly},
KubernetesGroups: kubernetesGroups,
KubernetesUsers: kubernetesUsers,
}
subject, err := identity.Subject()
if err != nil {
Expand Down
1 change: 1 addition & 0 deletions lib/auth/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,7 @@ func (a *AuthMiddleware) GetUser(r *http.Request) (IdentityGetter, error) {
Username: identity.Username,
Principals: identity.Principals,
KubernetesGroups: identity.KubernetesGroups,
KubernetesUsers: identity.KubernetesUsers,
RemoteRoles: identity.Groups,
Identity: *identity,
}, nil
Expand Down
9 changes: 7 additions & 2 deletions lib/auth/permissions.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,15 +147,17 @@ func (a *authorizer) authorizeRemoteUser(u RemoteUser) (*AuthContext, error) {
return nil, trace.AccessDenied("no roles mapped for remote user %q from cluster %q", u.Username, u.ClusterName)
}
// Set "logins" trait and "kubernetes_groups" for the remote user. This allows Teleport to work by
// passing exact logins and kubernetes groups to the remote cluster. Note that claims (OIDC/SAML)
// passing exact logins, kubernetes groups and users to the remote cluster. Note that claims (OIDC/SAML)
// are not passed, but rather the exact logins, this is done to prevent
// leaking too much of identity to the remote cluster, and instead of focus
// on main cluster's interpretation of this identity
traits := map[string][]string{
teleport.TraitLogins: u.Principals,
teleport.TraitKubeGroups: u.KubernetesGroups,
teleport.TraitKubeUsers: u.KubernetesUsers,
}
log.Debugf("Mapped roles %v of remote user %q to local roles %v and traits %v.", u.RemoteRoles, u.Username, roleNames, traits)
log.Debugf("Mapped roles %v of remote user %q to local roles %v and traits %v.",
u.RemoteRoles, u.Username, roleNames, traits)
checker, err := services.FetchRoles(roleNames, a.access, traits)
if err != nil {
return nil, trace.Wrap(err)
Expand Down Expand Up @@ -589,6 +591,9 @@ type RemoteUser struct {
// KubernetesGroups is a list of Kubernetes groups
KubernetesGroups []string `json:"kubernetes_groups"`

// KubernetesUsers is a list of Kubernetes users
KubernetesUsers []string `json:"kubernetes_users"`

// Identity is source x509 used to build this role
Identity tlsca.Identity
}
Expand Down
Loading