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
28 changes: 20 additions & 8 deletions api/types/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -508,16 +508,21 @@ func (d *DatabaseV3) CheckAndSetDefaults() error {
return trace.BadParameter("database %q protocol is empty", d.GetName())
}
if d.Spec.URI == "" {
switch {
case d.IsAWSKeyspaces() && d.Spec.AWS.Region != "":
// In case of AWS Hosted Cassandra allow to omit URI.
// The URL will be constructed from the database resource based on the region and account ID.
d.Spec.URI = awsutils.CassandraEndpointURLForRegion(d.Spec.AWS.Region)
case d.IsDynamoDB():
switch d.GetType() {
case DatabaseTypeAWSKeyspaces:
if d.Spec.AWS.Region != "" {
// In case of AWS Hosted Cassandra allow to omit URI.
// The URL will be constructed from the database resource based on the region and account ID.
d.Spec.URI = awsutils.CassandraEndpointURLForRegion(d.Spec.AWS.Region)
} else {
return trace.BadParameter("AWS Keyspaces database %q URI is empty and cannot be derived without a configured AWS region",
d.GetName())
}
case DatabaseTypeDynamoDB:
if d.Spec.AWS.Region != "" {
d.Spec.URI = awsutils.DynamoDBURIForRegion(d.Spec.AWS.Region)
} else {
return trace.BadParameter("DynamoDB database %q URI is missing and cannot be derived from an empty configured AWS region",
return trace.BadParameter("DynamoDB database %q URI is empty and cannot be derived without a configured AWS region",
d.GetName())
}
default:
Expand Down Expand Up @@ -679,6 +684,13 @@ func (d *DatabaseV3) CheckAndSetDefaults() error {
}
}

if d.Spec.AWS.ExternalID != "" && d.Spec.AWS.AssumeRoleARN == "" && !d.RequireAWSIAMRolesAsUsers() {
// Databases that use database username to assume an IAM role do not
// need assume_role_arn in configuration when external_id is set.
return trace.BadParameter("AWS database %q has external_id %q, but assume_role_arn is empty",
d.GetName(), d.Spec.AWS.ExternalID)
}

// Validate Cloud SQL specific configuration.
switch {
case d.Spec.GCP.ProjectID != "" && d.Spec.GCP.InstanceID == "":
Expand All @@ -704,7 +716,7 @@ func (d *DatabaseV3) handleDynamoDBConfig() error {
// so we check if the region is configured to see if this is really a configuration error.
if d.Spec.AWS.Region == "" {
// the AWS region is empty and we can't derive it from the URI, so this is a config error.
return trace.BadParameter("database %q AWS region is missing and cannot be derived from the URI %q",
return trace.BadParameter("database %q AWS region is empty and cannot be derived from the URI %q",
d.GetName(), d.Spec.URI)
}
if awsutils.IsAWSEndpoint(d.Spec.URI) {
Expand Down
48 changes: 44 additions & 4 deletions api/types/database_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,8 @@ func TestDynamoDBConfig(t *testing.T) {
uri string
region string
account string
roleARN string
externalID string
wantSpec DatabaseSpecV3
wantErrMsg string
}{
Expand All @@ -564,6 +566,22 @@ func TestDynamoDBConfig(t *testing.T) {
},
},
},
{
desc: "account and region and assume role is correct",
region: "us-west-1",
account: "123456789012",
roleARN: "arn:aws:iam::123456789012:role/DBDiscoverer",
externalID: "externalid123",
wantSpec: DatabaseSpecV3{
URI: "aws://dynamodb.us-west-1.amazonaws.com",
AWS: AWS{
Region: "us-west-1",
AccountID: "123456789012",
AssumeRoleARN: "arn:aws:iam::123456789012:role/DBDiscoverer",
Comment thread
greedy52 marked this conversation as resolved.
ExternalID: "externalid123",
},
},
},
{
desc: "account and AWS URI and empty region is correct",
uri: "dynamodb.us-west-1.amazonaws.com",
Expand Down Expand Up @@ -626,6 +644,21 @@ func TestDynamoDBConfig(t *testing.T) {
},
},
},
{
desc: "configured external ID but not assume role is ok",
uri: "localhost:8080",
region: "us-west-1",
account: "123456789012",
externalID: "externalid123",
wantSpec: DatabaseSpecV3{
URI: "localhost:8080",
AWS: AWS{
Region: "us-west-1",
AccountID: "123456789012",
ExternalID: "externalid123",
},
},
},
{
desc: "region and different AWS URI region is an error",
uri: "dynamodb.us-west-2.amazonaws.com",
Expand All @@ -644,12 +677,12 @@ func TestDynamoDBConfig(t *testing.T) {
desc: "custom URI and missing region is an error",
uri: "localhost:8080",
account: "123456789012",
wantErrMsg: "region is missing",
wantErrMsg: "region is empty",
},
{
desc: "missing URI and missing region is an error",
account: "123456789012",
wantErrMsg: "URI is missing",
wantErrMsg: "URI is empty",
},
{
desc: "invalid AWS account ID is an error",
Expand All @@ -658,6 +691,11 @@ func TestDynamoDBConfig(t *testing.T) {
account: "12345",
wantErrMsg: "must be 12-digit",
},
{
region: "us-west-1",
desc: "missing account id",
wantErrMsg: "account ID is empty",
},
}

for _, tt := range tests {
Expand All @@ -670,8 +708,10 @@ func TestDynamoDBConfig(t *testing.T) {
Protocol: "dynamodb",
URI: tt.uri,
AWS: AWS{
Region: tt.region,
AccountID: tt.account,
Region: tt.region,
AccountID: tt.account,
AssumeRoleARN: tt.roleARN,
ExternalID: tt.externalID,
},
})
if tt.wantErrMsg != "" {
Expand Down
30 changes: 21 additions & 9 deletions lib/config/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ type CommandLineFlags struct {
DatabaseAWSRegion string
// DatabaseAWSAccountID is an optional AWS account ID e.g. when using Keyspaces.
DatabaseAWSAccountID string
// DatabaseAWSAssumeRoleARN is an optional AWS IAM role ARN to assume when accessing the database.
DatabaseAWSAssumeRoleARN string
// DatabaseAWSExternalID is an optional AWS external ID used to enable assuming an AWS role across accounts.
DatabaseAWSExternalID string
// DatabaseAWSRedshiftClusterID is Redshift cluster identifier.
Expand Down Expand Up @@ -1215,9 +1217,13 @@ func applyDiscoveryConfig(fc *FileConfig, cfg *servicecfg.Config) error {
services.AWSMatcher{
Types: matcher.Types,
Regions: matcher.Regions,
Tags: matcher.Tags,
Params: installParams,
SSM: &services.AWSSSM{DocumentName: matcher.SSM.DocumentName},
AssumeRole: services.AssumeRole{
RoleARN: matcher.AssumeRoleARN,
ExternalID: matcher.ExternalID,
},
Tags: matcher.Tags,
Params: installParams,
SSM: &services.AWSSSM{DocumentName: matcher.SSM.DocumentName},
})
}

Expand Down Expand Up @@ -1321,6 +1327,10 @@ func applyDatabasesConfig(fc *FileConfig, cfg *servicecfg.Config) error {
Types: matcher.Types,
Regions: matcher.Regions,
Tags: matcher.Tags,
AssumeRole: services.AssumeRole{
RoleARN: matcher.AssumeRoleARN,
ExternalID: matcher.ExternalID,
},
})
}
for _, matcher := range fc.Databases.AzureMatchers {
Expand Down Expand Up @@ -1370,9 +1380,10 @@ func applyDatabasesConfig(fc *FileConfig, cfg *servicecfg.Config) error {
Mode: servicecfg.TLSMode(database.TLS.Mode),
},
AWS: servicecfg.DatabaseAWS{
AccountID: database.AWS.AccountID,
ExternalID: database.AWS.ExternalID,
Region: database.AWS.Region,
AccountID: database.AWS.AccountID,
AssumeRoleARN: database.AWS.AssumeRoleARN,
ExternalID: database.AWS.ExternalID,
Region: database.AWS.Region,
Redshift: servicecfg.DatabaseAWSRedshift{
ClusterID: database.AWS.Redshift.ClusterID,
},
Expand Down Expand Up @@ -1905,9 +1916,10 @@ func Configure(clf *CommandLineFlags, cfg *servicecfg.Config, legacyAppFlags boo
CACert: caBytes,
},
AWS: servicecfg.DatabaseAWS{
Region: clf.DatabaseAWSRegion,
AccountID: clf.DatabaseAWSAccountID,
ExternalID: clf.DatabaseAWSExternalID,
Region: clf.DatabaseAWSRegion,
AccountID: clf.DatabaseAWSAccountID,
AssumeRoleARN: clf.DatabaseAWSAssumeRoleARN,
ExternalID: clf.DatabaseAWSExternalID,
Redshift: servicecfg.DatabaseAWSRedshift{
ClusterID: clf.DatabaseAWSRedshiftClusterID,
},
Expand Down
Loading