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

[v9] Add MariaDB to AWS RDS auto discovery #10994

Merged
merged 2 commits into from
Mar 9, 2022
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
16 changes: 9 additions & 7 deletions docs/pages/database-access/guides/rds.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
title: Database Access with AWS RDS and Aurora for PostgreSQL and MySQL
description: How to configure Teleport Database Access with AWS RDS and Aurora for PostgreSQL and MySQL.
title: Database Access with AWS RDS and Aurora
h1: Database Access with AWS RDS and Aurora for PostgreSQL, MySQL and MariaDB
description: How to configure Teleport Database Access with AWS RDS and Aurora for PostgreSQL, MySQL and MariaDB.
---

This guide will help you to:
Expand All @@ -9,9 +10,10 @@ This guide will help you to:
- Set up Teleport to access your RDS instances and Aurora clusters.
- Connect to your databases through Teleport.

<Admonition type="note" title="Aurora Serverless">
Aurora Serverless does not support IAM authentication at the time of this
writing so it can't be used with Database Access.
<Admonition type="note" title="Supported versions">
The following products are not compatible with Database Access as they don't support IAM authentication:
- Aurora Serverless.
- RDS MariaDB versions lower than 10.6.
</Admonition>

## Prerequisites
Expand Down Expand Up @@ -318,8 +320,8 @@ Access for RDS. See below how to enable it for your database engine.
GRANT rds_iam TO alice;
```
</TabItem>
<TabItem label="MySQL">
MySQL users must have RDS authentication plugin enabled:
<TabItem label="MySQL/MariaDB">
MySQL and MariaDB users must have the RDS authentication plugin enabled:

```sql
CREATE USER alice IDENTIFIED WITH AWSAuthenticationPlugin AS 'RDS';
Expand Down
32 changes: 28 additions & 4 deletions lib/services/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
"github.com/aws/aws-sdk-go/service/rds"
"github.com/aws/aws-sdk-go/service/redshift"

"github.com/coreos/go-semver/semver"
"github.com/gravitational/trace"
log "github.com/sirupsen/logrus"
)
Expand All @@ -49,7 +50,7 @@ type Databases interface {
DatabaseGetter
// CreateDatabase creates a new database resource.
CreateDatabase(context.Context, types.Database) error
// UpdateDatabse updates an existing database resource.
// UpdateDatabase updates an existing database resource.
UpdateDatabase(context.Context, types.Database) error
// DeleteDatabase removes the specified database resource.
DeleteDatabase(ctx context.Context, name string) error
Expand Down Expand Up @@ -290,7 +291,7 @@ func engineToProtocol(engine string) string {
switch engine {
case RDSEnginePostgres, RDSEngineAuroraPostgres:
return defaults.ProtocolPostgres
case RDSEngineMySQL, RDSEngineAurora, RDSEngineAuroraMySQL:
case RDSEngineMySQL, RDSEngineAurora, RDSEngineAuroraMySQL, RDSEngineMariaDB:
return defaults.ProtocolMySQL
}
return ""
Expand Down Expand Up @@ -369,6 +370,27 @@ func rdsTagsToLabels(tags []*rds.Tag) map[string]string {
return labels
}

// IsRDSInstanceSupported returns true if database supports IAM authentication.
// Currently, only MariaDB is being checked.
func IsRDSInstanceSupported(instance *rds.DBInstance) bool {
// TODO(jakule): Check other engines.
if aws.StringValue(instance.Engine) != RDSEngineMariaDB {
return true
}

// MariaDB follows semver schema: https://mariadb.org/about/
ver, err := semver.NewVersion(aws.StringValue(instance.EngineVersion))
if err != nil {
log.Errorf("Failed to parse RDS MariaDB version: %s", aws.StringValue(instance.EngineVersion))
return false
}

// Min supported MariaDB version that supports IAM is 10.6
// https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.html
minIAMSupportedVer := semver.New("10.6.0")
return !ver.LessThan(*minIAMSupportedVer)
}

// IsRDSClusterSupported checks whether the aurora cluster is supported and logs
// related info if not.
func IsRDSClusterSupported(cluster *rds.DBCluster) bool {
Expand Down Expand Up @@ -536,6 +558,8 @@ const (
RDSEngineMySQL = "mysql"
// RDSEnginePostgres is RDS engine name for Postgres instances.
RDSEnginePostgres = "postgres"
// RDSEngineMariaDB is RDS engine name for MariaDB instances.
RDSEngineMariaDB = "mariadb"
// RDSEngineAurora is RDS engine name for Aurora MySQL 5.6 compatible clusters.
RDSEngineAurora = "aurora"
// RDSEngineAuroraMySQL is RDS engine name for Aurora MySQL 5.7 compatible clusters.
Expand All @@ -553,9 +577,9 @@ const (
// RDSEndpointTypeReader is the endpoint that load-balances connections across the Aurora Replicas that are
// available in a RDS cluster.
RDSEndpointTypeReader RDSEndpointType = "reader"
// RDSEndpointTypeCustom is the endpoint that specifieds one of the custom endpoints associated with the RDS cluster.
// RDSEndpointTypeCustom is the endpoint that specifies one of the custom endpoints associated with the RDS cluster.
RDSEndpointTypeCustom RDSEndpointType = "custom"
// RDSEndpointTypeInstance is the endpoint of a RDS DB instance.
// RDSEndpointTypeInstance is the endpoint of an RDS DB instance.
RDSEndpointTypeInstance RDSEndpointType = "instance"
)

Expand Down
51 changes: 50 additions & 1 deletion lib/services/database_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -358,8 +358,57 @@ func TestIsRDSClusterSupported(t *testing.T) {
EngineVersion: aws.String(test.engineVersion),
}

require.Equal(t, test.isSupported, IsRDSClusterSupported(cluster))
got, want := IsRDSClusterSupported(cluster), test.isSupported
require.Equal(t, want, got, "IsRDSClusterSupported = %v, want = %v", got, want)
})
}
}

func TestIsRDSInstanceSupported(t *testing.T) {
tests := []struct {
name string
engine string
engineVersion string
isSupported bool
}{
{
name: "non-MariaDB engine",
engine: RDSEnginePostgres,
engineVersion: "13.3",
isSupported: true,
},
{
name: "unsupported MariaDB",
engine: RDSEngineMariaDB,
engineVersion: "10.3.28",
isSupported: false,
},
{
name: "min supported version",
engine: RDSEngineMariaDB,
engineVersion: "10.6.2",
isSupported: true,
},
{
name: "supported version",
engine: RDSEngineMariaDB,
engineVersion: "10.8.0",
isSupported: true,
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
cluster := &rds.DBInstance{
DBInstanceArn: aws.String("arn:aws:rds:us-east-1:1234567890:instance:test"),
DBClusterIdentifier: aws.String(test.name),
DbiResourceId: aws.String(uuid.New().String()),
Engine: aws.String(test.engine),
EngineVersion: aws.String(test.engineVersion),
}

got, want := IsRDSInstanceSupported(cluster), test.isSupported
require.Equal(t, want, got, "IsRDSInstanceSupported = %v, want = %v", got, want)
})
}
}
Expand Down
11 changes: 10 additions & 1 deletion lib/srv/db/cloud/watchers/rds.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,14 @@ func (f *rdsDBInstancesFetcher) getRDSDatabases(ctx context.Context) (types.Data
}
databases := make(types.Databases, 0, len(instances))
for _, instance := range instances {
if !services.IsRDSInstanceSupported(instance) {
f.log.Debugf("RDS instance %q (engine mode %v, engine version %v) doesn't support IAM authentication. Skipping.",
aws.StringValue(instance.DBInstanceIdentifier),
aws.StringValue(instance.Engine),
aws.StringValue(instance.EngineVersion))
continue
}

if !services.IsRDSInstanceAvailable(instance) {
f.log.Debugf("The current status of RDS instance %q is %q. Skipping.",
aws.StringValue(instance.DBInstanceIdentifier),
Expand Down Expand Up @@ -275,7 +283,8 @@ func rdsFilters() []*rds.Filter {
Name: aws.String("engine"),
Values: aws.StringSlice([]string{
services.RDSEnginePostgres,
services.RDSEngineMySQL}),
services.RDSEngineMySQL,
services.RDSEngineMariaDB}),
}}
}

Expand Down