mirror of
https://github.com/gravitational/teleport
synced 2024-10-21 17:53:28 +00:00
Allow users impersonating database service generate database certs (#7024)
This commit is contained in:
parent
593e0e6145
commit
fc4c18f297
|
@ -2628,17 +2628,49 @@ func (a *ServerWithRoles) SignDatabaseCSR(ctx context.Context, req *proto.Databa
|
|||
}
|
||||
|
||||
// GenerateDatabaseCert generates a certificate used by a database service
|
||||
// to authenticate with the database instance
|
||||
// to authenticate with the database instance.
|
||||
//
|
||||
// This certificate can be requested by:
|
||||
//
|
||||
// - Cluster administrator using "tctl auth sign --format=db" command locally
|
||||
// on the auth server to produce a certificate for configuring a self-hosted
|
||||
// database.
|
||||
// - Remote user using "tctl auth sign --format=db" command with a remote
|
||||
// proxy (e.g. Teleport Cloud), as long as they can impersonate system
|
||||
// role Db.
|
||||
// - Database service when initiating connection to a database instance to
|
||||
// produce a client certificate.
|
||||
func (a *ServerWithRoles) GenerateDatabaseCert(ctx context.Context, req *proto.DatabaseCertRequest) (*proto.DatabaseCertResponse, error) {
|
||||
// This certificate can be requested only by a database service when
|
||||
// initiating connection to a database instance, or by an admin when
|
||||
// generating certificates for a database instance.
|
||||
if !a.hasBuiltinRole(string(teleport.RoleDatabase)) && !a.hasBuiltinRole(string(teleport.RoleAdmin)) {
|
||||
return nil, trace.AccessDenied("this request can only be executed by a database service or an admin")
|
||||
// Check if this is a local cluster admin, or a datababase service, or a
|
||||
// user that is allowed to impersonate database service.
|
||||
if !a.hasBuiltinRole(string(types.RoleDatabase)) && !a.hasBuiltinRole(string(types.RoleAdmin)) {
|
||||
if err := a.canImpersonateBuiltinRole(types.RoleDatabase); err != nil {
|
||||
log.WithError(err).Warnf("User %v tried to generate database certificate but is not allowed to impersonate %q system role.",
|
||||
a.context.User.GetName(), types.RoleDatabase)
|
||||
return nil, trace.AccessDenied("access denied")
|
||||
}
|
||||
}
|
||||
return a.authServer.GenerateDatabaseCert(ctx, req)
|
||||
}
|
||||
|
||||
// canImpersonateBuiltinRole checks if the current user can impersonate the
|
||||
// provided system role.
|
||||
func (a *ServerWithRoles) canImpersonateBuiltinRole(role types.SystemRole) error {
|
||||
roleCtx, err := NewBuiltinRoleContext(role)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
roleSet, ok := roleCtx.Checker.(BuiltinRoleSet)
|
||||
if !ok {
|
||||
return trace.BadParameter("expected BuiltinRoleSet, got %T", roleCtx.Checker)
|
||||
}
|
||||
err = a.context.Checker.CheckImpersonate(a.context.User, roleCtx.User, roleSet.RoleSet.WithoutImplicit())
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetAppServers gets all application servers.
|
||||
func (a *ServerWithRoles) GetAppServers(ctx context.Context, namespace string, opts ...services.MarshalOption) ([]services.Server, error) {
|
||||
if err := a.action(namespace, services.KindAppServer, services.VerbList); err != nil {
|
||||
|
|
|
@ -18,9 +18,11 @@ package auth
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509/pkix"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gravitational/teleport"
|
||||
"github.com/gravitational/teleport/api/client/proto"
|
||||
"github.com/gravitational/teleport/api/constants"
|
||||
"github.com/gravitational/teleport/api/types"
|
||||
|
@ -60,6 +62,71 @@ func TestSSOUserCanReissueCert(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// TestGenerateDatabaseCert makes sure users and services with appropriate
|
||||
// permissions can generate certificates for self-hosted databases.
|
||||
func TestGenerateDatabaseCert(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
srv := newTestTLSServer(t)
|
||||
|
||||
// This user can't impersonate anyone and can't generate database certs.
|
||||
userWithoutAccess, _, err := CreateUserAndRole(srv.Auth(), "user", []string{"role1"})
|
||||
require.NoError(t, err)
|
||||
|
||||
// This user can impersonate system role Db.
|
||||
userImpersonateDb, roleDb, err := CreateUserAndRole(srv.Auth(), "user-impersonate-db", []string{"role2"})
|
||||
require.NoError(t, err)
|
||||
roleDb.SetImpersonateConditions(types.Allow, types.ImpersonateConditions{
|
||||
Users: []string{string(types.RoleDatabase)},
|
||||
Roles: []string{string(types.RoleDatabase)},
|
||||
})
|
||||
require.NoError(t, srv.Auth().UpsertRole(ctx, roleDb))
|
||||
|
||||
tests := []struct {
|
||||
desc string
|
||||
identity TestIdentity
|
||||
err string
|
||||
}{
|
||||
{
|
||||
desc: "user can't sign database certs",
|
||||
identity: TestUser(userWithoutAccess.GetName()),
|
||||
err: "access denied",
|
||||
},
|
||||
{
|
||||
desc: "user can impersonate Db and sign database certs",
|
||||
identity: TestUser(userImpersonateDb.GetName()),
|
||||
},
|
||||
{
|
||||
desc: "built-in admin can sign database certs",
|
||||
identity: TestAdmin(),
|
||||
},
|
||||
{
|
||||
desc: "database service can sign database certs",
|
||||
identity: TestBuiltin(teleport.RoleDatabase),
|
||||
},
|
||||
}
|
||||
|
||||
// Generate CSR once for speed sake.
|
||||
priv, _, err := srv.Auth().GenerateKeyPair("")
|
||||
require.NoError(t, err)
|
||||
csr, err := tlsca.GenerateCertificateRequestPEM(pkix.Name{CommonName: "test"}, priv)
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
client, err := srv.NewClient(test.identity)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = client.GenerateDatabaseCert(ctx, &proto.DatabaseCertRequest{CSR: csr})
|
||||
if test.err != "" {
|
||||
require.EqualError(t, err, test.err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestSetAuthPreference tests the dynamic configuration rules described
|
||||
// in rfd/0016-dynamic-configuration.md § Implementation.
|
||||
func TestSetAuthPreference(t *testing.T) {
|
||||
|
|
|
@ -35,7 +35,12 @@ import (
|
|||
|
||||
// NewAdminContext returns new admin auth context
|
||||
func NewAdminContext() (*Context, error) {
|
||||
authContext, err := contextForBuiltinRole(BuiltinRole{Role: teleport.RoleAdmin, Username: fmt.Sprintf("%v", teleport.RoleAdmin)}, nil)
|
||||
return NewBuiltinRoleContext(types.RoleAdmin)
|
||||
}
|
||||
|
||||
// NewBuiltinRoleContext create auth context for the provided builtin role.
|
||||
func NewBuiltinRoleContext(role types.SystemRole) (*Context, error) {
|
||||
authContext, err := contextForBuiltinRole(BuiltinRole{Role: role, Username: fmt.Sprintf("%v", role)}, nil)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
|
|
@ -18,8 +18,8 @@ package services
|
|||
|
||||
import (
|
||||
"github.com/gravitational/teleport"
|
||||
|
||||
"github.com/gravitational/teleport/lib/defaults"
|
||||
|
||||
"github.com/pborman/uuid"
|
||||
)
|
||||
|
||||
|
|
|
@ -1124,6 +1124,17 @@ func (set RoleSet) HasRole(role string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// WithoutImplicit returns this role set with default implicit role filtered out.
|
||||
func (set RoleSet) WithoutImplicit() (out RoleSet) {
|
||||
for _, r := range set {
|
||||
if r.GetName() == teleport.DefaultImplicitRole {
|
||||
continue
|
||||
}
|
||||
out = append(out, r)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// AdjustSessionTTL will reduce the requested ttl to lowest max allowed TTL
|
||||
// for this role set, otherwise it returns ttl unchanged
|
||||
func (set RoleSet) AdjustSessionTTL(ttl time.Duration) time.Duration {
|
||||
|
|
Loading…
Reference in a new issue