diff --git a/aws/databases.go b/aws/databases.go index 0955d21..8d5dcda 100644 --- a/aws/databases.go +++ b/aws/databases.go @@ -54,6 +54,7 @@ type Database struct { Protocol string Public string Size string + Roles string } func (m *DatabasesModule) PrintDatabases(outputDirectory string, verbosity int) { @@ -113,7 +114,8 @@ func (m *DatabasesModule) PrintDatabases(outputDirectory string, verbosity int) "Size", "UserName", "Endpoint", - //"Port", + "Port", + "Roles", //"Protocol", //"Public", @@ -140,6 +142,8 @@ func (m *DatabasesModule) PrintDatabases(outputDirectory string, verbosity int) "Size", "UserName", "Endpoint", + "Port", + "Roles", } // Otherwise, use the default columns. } else { @@ -151,6 +155,7 @@ func (m *DatabasesModule) PrintDatabases(outputDirectory string, verbosity int) "Size", "UserName", "Endpoint", + "Port", } } @@ -167,7 +172,8 @@ func (m *DatabasesModule) PrintDatabases(outputDirectory string, verbosity int) m.Databases[i].Size, m.Databases[i].UserName, m.Databases[i].Endpoint, - // strconv.Itoa(int(m.Databases[i].Port)), + strconv.Itoa(int(m.Databases[i].Port)), + m.Databases[i].Roles, // m.Databases[i].Protocol, // m.Databases[i].Public, }, @@ -221,11 +227,11 @@ func (m *DatabasesModule) executeChecks(r string, wg *sync.WaitGroup, semaphore serviceMap := &awsservicemap.AwsServiceMap{ JsonFileSource: "DOWNLOAD_FROM_AWS", } - m.executeRdsCheck(r, wg, semaphore, dataReceiver, serviceMap) + m.executeRdsCheck(r, wg, semaphore, dataReceiver, serviceMap) // Also returns Neptune and DocDB databases m.executeRedshiftCheck(r, wg, semaphore, dataReceiver, serviceMap) m.executeDynamoDbCheck(r, wg, semaphore, dataReceiver, serviceMap) - m.executeDocDbCheck(r, wg, semaphore, dataReceiver, serviceMap) - m.executeNeptuneCheck(r, wg, semaphore, dataReceiver, serviceMap) + //m.executeDocDbCheck(r, wg, semaphore, dataReceiver, serviceMap) + //m.executeNeptuneCheck(r, wg, semaphore, dataReceiver, serviceMap) } type check struct { @@ -358,43 +364,56 @@ func (m *DatabasesModule) getRdsClustersPerRegion(r string, wg *sync.WaitGroup, m.CommandCounter.Pending-- m.CommandCounter.Executing++ - DBInstances, err := sdk.CachedRDSDescribeDBInstances(m.RDSClient, aws.ToString(m.Caller.Account), r) + DBClusters, err := sdk.CachedRDSDescribeDBClusters(m.RDSClient, aws.ToString(m.Caller.Account), r) if err != nil { - if errors.As(err, &oe) { - m.Errors = append(m.Errors, fmt.Sprintf(" Error: Region: %s, Service: %s, Operation: %s", r, oe.Service(), oe.Operation())) - } m.modLog.Error(err.Error()) m.CommandCounter.Error++ return } - var public string - for _, instance := range DBInstances { - if instance.Endpoint == nil || isNeptune(instance.Engine) { + for _, cluster := range DBClusters { + var public string + var service string + var roles string + if cluster.Endpoint == nil { continue } - name := aws.ToString(instance.DBInstanceIdentifier) - port := instance.Endpoint.Port - endpoint := aws.ToString(instance.Endpoint.Address) - engine := aws.ToString(instance.Engine) + name := aws.ToString(cluster.DBClusterIdentifier) + port := cluster.Port + endpoint := aws.ToString(cluster.Endpoint) + engine := aws.ToString(cluster.Engine) - if aws.ToBool(instance.PubliclyAccessible) { + if aws.ToBool(cluster.PubliclyAccessible) { public = "True" } else { public = "False" } + if isNeptune(cluster.Engine) { + service = "Neptune" + } else if isDocDB(cluster.Engine) { + service = "DocsDB" + } else { + service = "RDS" + } + + associatedRoles := cluster.AssociatedRoles + for _, role := range associatedRoles { + roles = roles + aws.ToString(role.RoleArn) + " " + } + dataReceiver <- Database{ - AWSService: "RDS", + AWSService: service, Region: r, Name: name, Engine: engine, Endpoint: endpoint, - UserName: aws.ToString(instance.MasterUsername), + UserName: aws.ToString(cluster.MasterUsername), Port: aws.ToInt32(port), - Protocol: aws.ToString(instance.Engine), + Protocol: aws.ToString(cluster.Engine), Public: public, + Roles: roles, } } } @@ -432,19 +451,19 @@ func (m *DatabasesModule) getRedshiftDatabasesPerRegion(r string, wg *sync.WaitG name := aws.ToString(cluster.DBName) //id := workspace.Id endpoint := aws.ToString(cluster.Endpoint.Address) + port := aws.ToInt32(cluster.Endpoint.Port) if aws.ToBool(cluster.PubliclyAccessible) { public = "True" } else { public = "False" } - port := cluster.Endpoint.Port dataReceiver <- Database{ AWSService: awsService, Region: r, Name: name, Endpoint: endpoint, - Port: aws.ToInt32(port), + Port: port, Protocol: protocol, Public: public, } @@ -586,3 +605,7 @@ func (m *DatabasesModule) getNeptuneDatabasesPerRegion(r string, wg *sync.WaitGr func isNeptune(engine *string) bool { return *engine == "neptune" } + +func isDocDB(engine *string) bool { + return *engine == "docdb" +} diff --git a/aws/sdk/docdb.go b/aws/sdk/docdb.go index e7273fc..d958991 100644 --- a/aws/sdk/docdb.go +++ b/aws/sdk/docdb.go @@ -21,7 +21,7 @@ type DocDBClientInterface interface { func init() { gob.Register([]docdbTypes.GlobalCluster{}) - gob.Register([]docdbTypes.DBCluster{}) + //gob.Register([]docdbTypes.DBCluster{}) //gob.Register([]docdbTypes.DBInstance{}) } diff --git a/aws/sdk/neptune.go b/aws/sdk/neptune.go index d4c7254..307b788 100644 --- a/aws/sdk/neptune.go +++ b/aws/sdk/neptune.go @@ -2,7 +2,6 @@ package sdk import ( "context" - "encoding/gob" "fmt" "github.com/BishopFox/cloudfox/internal" @@ -17,7 +16,7 @@ type NeptuneClientInterface interface { } func init() { - gob.RegisterName("neptune.DBCluster", []neptuneTypes.DBCluster{}) + //gob.RegisterName("neptune.DBCluster", []neptuneTypes.DBCluster{}) } func CachedNeptuneDescribeDBClusters(client NeptuneClientInterface, accountID string, region string) ([]neptuneTypes.DBCluster, error) { diff --git a/aws/sdk/rds.go b/aws/sdk/rds.go index bcb4d51..d3339de 100644 --- a/aws/sdk/rds.go +++ b/aws/sdk/rds.go @@ -14,10 +14,13 @@ import ( type RDSClientInterface interface { DescribeDBInstances(context.Context, *rds.DescribeDBInstancesInput, ...func(*rds.Options)) (*rds.DescribeDBInstancesOutput, error) + DescribeDBClusters(context.Context, *rds.DescribeDBClustersInput, ...func(*rds.Options)) (*rds.DescribeDBClustersOutput, error) } func init() { gob.Register([]rdsTypes.DBInstance{}) + gob.Register([]rdsTypes.DBCluster{}) + } func CachedRDSDescribeDBInstances(client RDSClientInterface, accountID string, region string) ([]rdsTypes.DBInstance, error) { @@ -55,3 +58,39 @@ func CachedRDSDescribeDBInstances(client RDSClientInterface, accountID string, r internal.Cache.Set(cacheKey, instances, cache.DefaultExpiration) return instances, nil } + +func CachedRDSDescribeDBClusters(client RDSClientInterface, accountID string, region string) ([]rdsTypes.DBCluster, error) { + var PaginationControl *string + var clusters []rdsTypes.DBCluster + cacheKey := fmt.Sprintf("%s-rds-DescribeDBClusters-%s", accountID, region) + cached, found := internal.Cache.Get(cacheKey) + if found { + return cached.([]rdsTypes.DBCluster), nil + } + for { + DescribeDBClusters, err := client.DescribeDBClusters( + context.TODO(), + &rds.DescribeDBClustersInput{ + Marker: PaginationControl, + }, + func(o *rds.Options) { + o.Region = region + }, + ) + + if err != nil { + return clusters, err + } + + clusters = append(clusters, DescribeDBClusters.DBClusters...) + + //pagination + if DescribeDBClusters.Marker == nil { + break + } + PaginationControl = DescribeDBClusters.Marker + } + + internal.Cache.Set(cacheKey, clusters, cache.DefaultExpiration) + return clusters, nil +} diff --git a/aws/sdk/rds_mocks.go b/aws/sdk/rds_mocks.go index dc158dc..b6ec344 100644 --- a/aws/sdk/rds_mocks.go +++ b/aws/sdk/rds_mocks.go @@ -32,3 +32,53 @@ func (m *MockedRDSClient) DescribeDBInstances(ctx context.Context, input *rds.De }, }, nil } + +func (m *MockedRDSClient) DescribeDBClusters(ctx context.Context, input *rds.DescribeDBClustersInput, options ...func(*rds.Options)) (*rds.DescribeDBClustersOutput, error) { + return &rds.DescribeDBClustersOutput{ + DBClusters: []rdsTypes.DBCluster{ + { + DBClusterIdentifier: aws.String("db1"), + Engine: aws.String("aurora-postgresql"), + EngineVersion: aws.String("13.3"), + Endpoint: aws.String("db1.cluster-123456789012.us-west-2.rds.amazonaws.com"), + ClusterCreateTime: aws.Time(time.Now()), + MasterUsername: aws.String("postgres"), + Port: aws.Int32(5432), + AssociatedRoles: nil, + }, + { + DBClusterIdentifier: aws.String("db2"), + Engine: aws.String("aurora-postgresql"), + EngineVersion: aws.String("13.3"), + Endpoint: aws.String("db2.cluster-123456789012.us-west-2.rds.amazonaws.com"), + ClusterCreateTime: aws.Time(time.Now()), + MasterUsername: aws.String("postgres"), + Port: aws.Int32(5432), + AssociatedRoles: nil, + }, + { + DBClusterIdentifier: aws.String("db3"), + Engine: aws.String("neptune"), + EngineVersion: aws.String("1.2.1.0"), + Endpoint: aws.String("db3.cluster-123456789012.us-west-2.neptune.amazonaws.com"), + MasterUsername: aws.String("neptune"), + Port: aws.Int32(8182), + AssociatedRoles: []rdsTypes.DBClusterRole{ + { + RoleArn: aws.String("arn:aws:iam::123456789012:role/NeptuneRole"), + Status: aws.String("active"), + }, + }, + }, + { + DBClusterIdentifier: aws.String("db4"), + Engine: aws.String("docsdb"), + EngineVersion: aws.String("4.0.0"), + Endpoint: aws.String("db4.cluster-123456789012.us-west-2.docdb.amazonaws.com"), + MasterUsername: aws.String("docsdb"), + Port: aws.Int32(27017), + AssociatedRoles: nil, + }, + }, + }, nil +}