Skip to content
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
33 changes: 29 additions & 4 deletions lib/srv/db/access_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -281,13 +281,18 @@ func TestAccessMySQL(t *testing.T) {
// TestAccessRedis verifies access scenarios to a Redis database based
// on the configured RBAC rules.
func TestAccessRedis(t *testing.T) {
ctx := context.Background()
testCtx := setupTestContext(ctx, t, withSelfHostedRedis("redis"))
ctx, cancel := context.WithCancel(context.Background())
t.Cleanup(cancel)
testCtx := setupTestContext(ctx, t,
withSelfHostedRedis("redis"),
withAzureRedis("azure-redis", azureRedisToken))
go testCtx.startHandlingConnections()

tests := []struct {
// desc is the test case description.
desc string
// dbService is the name of the database service to connect to.
dbService string
// user is the Teleport local username the test will use.
user string
// role is the Teleport role name to create and assign to the user.
Expand All @@ -301,13 +306,15 @@ func TestAccessRedis(t *testing.T) {
}{
{
desc: "has access to all database users",
dbService: "redis",
user: "alice",
role: "admin",
allowDbUsers: []string{types.Wildcard},
dbUser: "root",
},
{
desc: "has access to nothing",
dbService: "redis",
user: "alice",
role: "admin",
allowDbUsers: []string{},
Expand All @@ -316,29 +323,47 @@ func TestAccessRedis(t *testing.T) {
},
{
desc: "access allowed to specific user",
dbService: "redis",
user: "alice",
role: "admin",
allowDbUsers: []string{"alice"},
dbUser: "alice",
},
{
desc: "access denied to specific user",
dbService: "redis",
user: "alice",
role: "admin",
allowDbUsers: []string{"alice"},
dbUser: "root",
err: "access to db denied",
},
{
desc: "azure access allowed to default user",
dbService: "azure-redis",
user: "alice",
role: "admin",
allowDbUsers: []string{"default"},
dbUser: "default",
},
{
desc: "azure access denied to non-default user",
dbService: "azure-redis",
user: "alice",
role: "admin",
allowDbUsers: []string{"alice"},
dbUser: "alice",
err: "access denied to non-default db user",
},
}

for _, test := range tests {
t.Run(test.desc, func(t *testing.T) {
// Create user/role with the requested permissions.
testCtx.createUserAndRole(ctx, t, test.user, test.role, test.allowDbUsers, []string{types.Wildcard})

ctx := context.Background()
// Try to connect to the database as this user.
redisClient, err := testCtx.redisClient(ctx, test.user, "redis", test.dbUser)
redisClient, err := testCtx.redisClient(ctx, test.user, test.dbService, test.dbUser)
if test.err != "" {
require.Error(t, err)
require.Contains(t, err.Error(), test.err)
Expand Down
22 changes: 22 additions & 0 deletions lib/srv/db/redis/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ func (e *Engine) InitializeConnection(clientConn net.Conn, sessionCtx *common.Se
// authorizeConnection does authorization check for Redis connection about
// to be established.
func (e *Engine) authorizeConnection(ctx context.Context) error {
if err := e.checkDefaultUserRequired(); err != nil {
e.Audit.OnSessionStart(e.Context, e.sessionCtx, err)
return trace.Wrap(err)
}
authPref, err := e.Auth.GetAuthPreference(ctx)
if err != nil {
return trace.Wrap(err)
Expand All @@ -104,6 +108,24 @@ func (e *Engine) authorizeConnection(ctx context.Context) error {
return nil
}

// checkDefaultUserRequired checks if the session db user is the "default" Redis
// user, and checks if the session db user must be the default user.
// When the session db user is not "default", but it's required to be "default",
// return an error.
func (e *Engine) checkDefaultUserRequired() error {
// When the db user is already "default", there's no need to check if it's
// required to be "default".
if defaults.DefaultRedisUsername == e.sessionCtx.DatabaseUser {
return nil
}
if e.sessionCtx.Database.IsAzure() {
return trace.AccessDenied("access denied to non-default db user: "+
"Azure Cache for Redis requires authentication as default user %q",
defaults.DefaultRedisUsername)
}
return nil
}

// SendError sends error message to connected client.
func (e *Engine) SendError(redisErr error) {
if redisErr == nil || utils.IsOKNetworkError(redisErr) {
Expand Down
4 changes: 2 additions & 2 deletions lib/srv/db/redis/protocol/resp2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,8 @@ func TestMakeUnknownCommandErrorForCmd(t *testing.T) {
}{
{
name: "HELLO",
command: []interface{}{"HELLO", 3, "user", "TOKEN"},
expectedError: "ERR unknown command 'HELLO', with args beginning with: '3' 'user' 'TOKEN'",
command: []interface{}{"HELLO", 3, "AUTH", "user", "TOKEN"},
expectedError: "ERR unknown command 'HELLO', with args beginning with: '3' 'AUTH' 'user' 'TOKEN'",
},
{
name: "no extra args",
Expand Down