# AWS Cross-Account Database Access

You can deploy the Teleport Database Service with AWS IAM credentials in one AWS account and use an AWS IAM role to grant Teleport access to databases in another AWS account.

When the Teleport Database Service needs to discover, configure, or retrieve short-lived authentication tokens for AWS databases, it uses credentials for an AWS IAM identity to make requests to the AWS API. To access resources across AWS accounts, you can configure the Teleport Database Service to assume an AWS role in another account before it uses the AWS API for further actions.

This is not limited to a single AWS role: the Teleport Database Service can be configured to connect to databases in its own AWS account and multiple external AWS accounts at the same time.

You will need to configure the Teleport Database Service to assume an AWS IAM role and ensure that AWS IAM permissions are configured to allow the `sts:AssumeRole` call.

---

NOTE

You should also check that your network configuration in AWS allows the Teleport Database Service to connect to the databases.

This guide does not cover AWS network configuration, because it depends on your specific AWS network setup and the kind(s) of AWS databases you wish to connect to Teleport. For more information, see [how to connect your database](https://goteleport.com/docs/enroll-resources/database-access/enrollment/aws.md).

---

## Teleport configuration

The Teleport Database Service must be configured to assume an external AWS IAM role and, optionally, pass an external ID when it assumes that role. The configured AWS IAM role will be assumed via an AWS STS `AssumeRole` call before the Teleport Database Service uses the AWS API to discover, configure, or retrieve short-lived authentication tokens for AWS databases.

An "external ID" is used to address what AWS calls [the confused deputy problem](https://docs.aws.amazon.com/IAM/latest/UserGuide/confused-deputy.html). When you configure the Teleport Database Service to use an external ID, it will include that external ID when it calls AWS STS `AssumeRole`. The external AWS IAM role's trust policy will be used to verify that the correct external ID was provided in the `AssumeRole` call. For information about when you should use an external ID, see: [purpose of an AWS external ID](https://docs.aws.amazon.com/iam/latest/userguide/id_roles_create_for-user_externalid.html#external-id-purpose).

AWS database discovery config, static database config, and dynamic database config all support the `assume_role_arn` and `external_id` settings.

**AWS Discovery**

Modify your Teleport Database Service configuration file to assume an external AWS IAM role when it is discovering AWS databases.

```
# This example configuration will discover Amazon RDS databases in us-west-1
# within AWS account `222222222222` by assuming the external AWS IAM role
# "example-role".
db_service:
  enabled: true
  aws:
    - types: ["rds"]
      regions: ["us-west-1"]
      assume_role_arn: "arn:aws:iam::222222222222:role/example-role"
      external_id: "example-external-id"

```

Restart the Teleport Database Service for the configuration file changes to take effect.

---

NOTE

The AWS IAM role used to discover a database will also be used by the Teleport Database Service to provide access to that database.

---

**Static Config**

Modify your Teleport Database Service configuration file to statically register an AWS database in an external account and proxy connections to it.

```
# This example configuration will statically register an RDS PostgreSQL instance
# in us-west-1 within AWS account `222222222222` by assuming an external AWS
# IAM role "example-role".
db_service:
  enabled: true
  databases:
  - name: "rds-postgres"
    protocol: "postgres"
    uri: "rds-postgres.abcdef012345.us-west-1.rds.amazonaws.com:5432"
    aws:
        assume_role_arn: "arn:aws:iam::222222222222:role/example-role"
        external_id: "example-external-id"

```

Restart the Teleport Database Service for the configuration file changes to take effect.

**Dynamic Config**

Create a dynamic database resource to dynamically register an AWS database in an external account and proxy connections to it.

```
# This example configuration will dynamically register an RDS PostgreSQL instance
# in us-west-1 within AWS account `222222222222`.
# Teleport Database Service agents that match its labels with resource selectors
# will proxy the database by assuming the configured external AWS IAM role.
kind: db
version: v3
metadata:
  name: "rds-postgres"
  description: "Example dynamic database resource"
  labels:
    env: "dev"
spec:
  protocol: "postgres"
  uri: "rds-postgres.abcdef012345.us-west-1.rds.amazonaws.com:5432"
  aws:
    # Note that account_id must match the AWS account ID in `assume_role_arn`.
    # Dynamic database resources do not derive `account_id` from
    # `assume_role_arn` automatically (unlike static configuration).
    account_id: "222222222222"
    assume_role_arn: "arn:aws:iam::222222222222:role/example-role"
    external_id: "example-external-id"

```

Save the configuration to a file like `database.yaml` and create it with `tctl`:

```
$ tctl create database.yaml
```

For more information about database registration using dynamic database resources, see: [Dynamic Registration](https://goteleport.com/docs/enroll-resources/database-access/guides/dynamic-registration.md).

## Teleport AWS IAM identity

In order to assume an AWS IAM role, the Teleport Database Service will need credentials for an AWS IAM identity of its own.

Grant the Database Service access to credentials that it can use to authenticate to AWS.

- If you are running the Database Service on an EC2 instance, you may use the EC2 Instance Metadata Service method
- If you are running the Database Service in Kubernetes, you can use IAM Roles for Service Accounts (IRSA)
- Otherwise, you must use environment variables

**Instance Metadata Service**

Teleport will detect when it is running on an EC2 instance and use the Instance Metadata Service to fetch credentials.

The EC2 instance should be configured to use an EC2 instance profile. For more information, see: [Using Instance Profiles](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_switch-role-ec2_instance-profiles.html).

**Kubernetes IRSA**

Refer to [IAM Roles for Service Accounts (IRSA)](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html) to set up an OIDC provider in AWS and configure an AWS IAM role that allows the pod's service account to assume the role.

**Environment Variables**

Teleport's built-in AWS client reads credentials from the following environment variables:

- `AWS_ACCESS_KEY_ID`
- `AWS_SECRET_ACCESS_KEY`
- `AWS_DEFAULT_REGION`

When you start the Database Service, the service reads environment variables from a file at the path `/etc/default/teleport`. Obtain these credentials from your organization. Ensure that `/etc/default/teleport` has the following content, replacing the values of each variable:

```
AWS_ACCESS_KEY_ID=00000000000000000000
AWS_SECRET_ACCESS_KEY=0000000000000000000000000000000000000000
AWS_DEFAULT_REGION=<YOUR_REGION>

```

Have multiple sources of AWS credentials?

Teleport's AWS client loads credentials from different sources in the following order:

- Environment Variables
- Shared credentials file
- Shared configuration file (Teleport always enables shared configuration)
- EC2 Instance Metadata (credentials only)

While you can provide AWS credentials via a shared credentials file or shared configuration file, you will need to run the Database Service with the `AWS_PROFILE` environment variable assigned to the name of your profile of choice.

If you have a specific use case that the instructions above do not account for, consult the documentation for the [AWS SDK for Go](https://docs.aws.amazon.com/sdk-for-go/api/aws/session/) for a detailed description of credential loading behavior.

## AWS IAM permissions

AWS IAM policies must be configured for both the Teleport Database Service's AWS IAM identity and the external AWS IAM role:

- The Teleport Database Service's AWS IAM identity must have permission to assume the external role.
- The external AWS IAM role's trust policy must trust the Teleport Database Service's AWS IAM identity.

Details

Why are both required?

Unlike assuming a role within the same AWS account, when an AWS IAM role is in a different AWS account than the IAM identity that attempts to assume it, the role's trust policy alone is not sufficient to allow assuming the role.

For more details, see: [AWS cross-account policy evaluation](https://docs.aws.amazon.com/iam/latest/userguide/reference_policies_evaluation-logic-cross-account.html#policy-eval-cross-account).

### Database Service IAM policy

Attach the following permission policy to the Teleport Database Service's AWS IAM identity:

```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "sts:AssumeRole",
            "Resource": "arn:aws:iam::222222222222:role/example-role"
        }
    ]
}

```

### External AWS IAM permission policy

You will also need to configure permissions for the external AWS IAM role, specific to the type of database(s) that it will be used to access.

**DocumentDB**

Assign aws-account-id to your AWS account ID:

```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "DocumentDBConnectAsIAMRole",
            "Effect": "Allow",
            "Action": "sts:AssumeRole",
            "Resource": [
                "arn:aws:iam::aws-account-id:role/DatabaseUserRole"
            ]
        },
        {
            "Sid": "DocumentDBCheckDomainURL",
            "Effect": "Allow",
            "Action": "rds:DescribeDBClusters",
            "Resource": "*"
        }
    ]
}

```

| Statement                    | Purpose                                                                        |
| ---------------------------- | ------------------------------------------------------------------------------ |
| `DocumentDBConnectAsIAMRole` | Assume an IAM role to connect to a DocumentDB cluster with IAM authentication. |
| `DocumentDBCheckDomainURL`   | Validate a domain's URL if it was auto-discovered by the Discovery Service.    |

**DynamoDB**

Assign aws-account-id to your AWS account ID:

```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "DynamoDBConnectAsIAMRole",
            "Effect": "Allow",
            "Action": "sts:AssumeRole",
            "Resource": [
                "arn:aws:iam::aws-account-id:role/DatabaseUserRole"
            ]
        },
        {
            "Sid": "DynamoDBSessionTagging",
            "Effect": "Allow",
            "Action": "sts:TagSession",
            "Resource": [
                "*"
            ]
        }
    ]
}

```

| Statement                  | Purpose                                                                                                          |
| -------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| `DynamoDBConnectAsIAMRole` | Assume an IAM role to forward requests to DynamoDB.                                                              |
| `DynamoDBSessionTagging`   | Tag assumed role sessions if tags are specified in the Teleport database configuration under `aws.session_tags`. |

The session tagging permissions are only required if you have configured tags under the `aws.session_tags` section of your Teleport database configuration.

**ElastiCache**

ElastiCache supports IAM authentication for Redis and Valkey engine version 7.0 or above. This is the recommended way to configure Teleport access to ElastiCache.

```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "ElastiCacheFetchMetadata",
            "Effect": "Allow",
            "Action": "elasticache:DescribeReplicationGroups",
            "Resource": "*"
        },
        {
            "Sid": "ElastiCacheDescribeUsers",
            "Effect": "Allow",
            "Action": "elasticache:DescribeUsers",
            "Resource": "*"
        },
        {
            "Sid": "ElastiCacheConnect",
            "Effect": "Allow",
            "Action": "elasticache:Connect",
            "Resource": "*"
        }
    ]
}

```

| Statement                  | Purpose                                                                                                         |
| -------------------------- | --------------------------------------------------------------------------------------------------------------- |
| `ElastiCacheFetchMetadata` | Automatically import AWS tags as database labels or find missing information such as the database's AWS region. |
| `ElastiCacheDescribeUsers` | Determine whether a user is compatible with IAM authentication.                                                 |
| `ElastiCacheConnect`       | Connect using IAM authentication.                                                                               |

You can reduce the scope of the `ElastiCacheConnect` statement by updating it to only allow specific ElastiCache clusters and IAM users. The resource ARN you can specify has the following formats:

```
arn:aws:elasticache:{Region}:{AccountID}:replicationgroup:{ReplicationGroup}
arn:aws:elasticache:{Region}:{AccountID}:user:{UserName}
```

See [Authenticating with IAM for ElastiCache](https://docs.aws.amazon.com/AmazonElastiCache/latest/red-ug/auth-iam.html) for more information.

**ElastiCache Serverless**

```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "ElastiCacheServerlessFetchMetadata",
            "Effect": "Allow",
            "Action": "elasticache:DescribeServerlessCaches",
            "Resource": "*"
        },
        {
            "Sid": "ElastiCacheServerlessDescribeUsers",
            "Effect": "Allow",
            "Action": "elasticache:DescribeUsers",
            "Resource": "*"
        },
        {
            "Sid": "ElastiCacheServerlessConnect",
            "Effect": "Allow",
            "Action": "elasticache:Connect",
            "Resource": "*"
        }
    ]
}

```

| Statement                            | Purpose                                                         |
| ------------------------------------ | --------------------------------------------------------------- |
| `ElastiCacheServerlessFetchMetadata` | Automatically import AWS metadata about the database.           |
| `ElastiCacheServerlessDescribeUsers` | Determine whether a user is compatible with IAM authentication. |
| `ElastiCacheServerlessConnect`       | Connect using IAM authentication.                               |

Details

You can reduce the scope of the `ElastiCacheServerlessConnect` statement by updating it to only allow specific ElastiCache Serverless caches and IAM users. The resource ARN you can specify has the following formats:

```
arn:aws:elasticache:{Region}:{AccountID}:serverlesscache:{CacheName}
arn:aws:elasticache:{Region}:{AccountID}:user:{UserName}
```

See [Authenticating with IAM for ElastiCache](https://docs.aws.amazon.com/AmazonElastiCache/latest/red-ug/auth-iam.html) for more information.

**Keyspaces**

Assign aws-account-id to your AWS account ID:

```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "KeyspacesConnectAsIAMRole",
            "Effect": "Allow",
            "Action": "sts:AssumeRole",
            "Resource": [
                "arn:aws:iam::aws-account-id:role/DatabaseUserRole"
            ]
        }
    ]
}

```

| Statement                   | Purpose                                              |
| --------------------------- | ---------------------------------------------------- |
| `KeyspacesConnectAsIAMRole` | Assume an IAM role to forward requests to Keyspaces. |

**MemoryDB**

MemoryDB supports IAM authentication for Redis engine version 7.0 or above. This is the recommended way to configure Teleport access to MemoryDB.

```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "MemoryDBFetchMetadata",
            "Effect": "Allow",
            "Action": "memorydb:DescribeClusters",
            "Resource": "*"
        },
        {
            "Sid": "MemoryDBDescribeUsers",
            "Effect": "Allow",
            "Action": "memorydb:DescribeUsers",
            "Resource": "*"
        },
        {
            "Sid": "MemoryDBConnect",
            "Effect": "Allow",
            "Action": "memorydb:Connect",
            "Resource": "*"
        }
    ]
}

```

| Statement               | Purpose                                                                                                         |
| ----------------------- | --------------------------------------------------------------------------------------------------------------- |
| `MemoryDBFetchMetadata` | Automatically import AWS tags as database labels or find missing information such as the database's AWS region. |
| `MemoryDBDescribeUsers` | Determine whether a user is compatible with IAM authentication.                                                 |
| `MemoryDBConnect`       | Connect using IAM authentication.                                                                               |

You can reduce the scope of the `MemoryDBConnect` statement by updating it to only allow specific MemoryDB clusters and IAM users. The resource ARN you can specify has the following formats:

```
arn:aws:memorydb:{Region}:{AccountID}:cluster:{ClusterName}
arn:aws:memorydb:{Region}:{AccountID}:user:{UserName}
```

See [Authenticating with IAM for MemoryDB](https://docs.aws.amazon.com/memorydb/latest/devguide/auth-iam.html) for more information.

**Opensearch**

Assign aws-account-id to your AWS account ID:

```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "OpenSearchCheckDomainURL",
            "Effect": "Allow",
            "Action": "es:DescribeDomains",
            "Resource": [
                "*"
            ]
        },
        {
            "Sid": "OpenSearchConnectAsIAMRole",
            "Effect": "Allow",
            "Action": "sts:AssumeRole",
            "Resource": [
                "arn:aws:iam::aws-account-id:role/DatabaseUserRole"
            ]
        }
    ]
}

```

| Statement                    | Purpose                                                                     |
| ---------------------------- | --------------------------------------------------------------------------- |
| `OpenSearchCheckDomainURL`   | Validate a domain's URL if it was auto-discovered by the Discovery Service. |
| `OpenSearchConnectAsIAMRole` | Assume an IAM role to forward requests to OpenSearch.                       |

**RDS**

```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "RDSAutoEnableIAMAuth",
            "Effect": "Allow",
            "Action": [
                "rds:ModifyDBCluster",
                "rds:ModifyDBInstance"
            ],
            "Resource": "*"
        },
        {
            "Sid": "RDSConnect",
            "Effect": "Allow",
            "Action": "rds-db:connect",
            "Resource": "*"
        },
        {
            "Sid": "RDSFetchMetadata",
            "Effect": "Allow",
            "Action": [
                "rds:DescribeDBClusters",
                "rds:DescribeDBInstances"
            ],
            "Resource": "*"
        }
    ]
}

```

| Statement              | Purpose                                                                                                         |
| ---------------------- | --------------------------------------------------------------------------------------------------------------- |
| `RDSAutoEnableIAMAuth` | Automatically enable IAM auth on RDS instances and Aurora clusters.                                             |
| `RDSConnect`           | Generate an IAM authentication token to connect to a database.                                                  |
| `RDSFetchMetadata`     | Automatically import AWS tags as database labels or find missing information such as the database's AWS region. |

The Teleport Database Service uses `rds:ModifyDBInstance` and `rds:ModifyDBCluster` to automatically enable [IAM authentication](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.html) on RDS instances and Aurora clusters, respectively. You can omit the `RDSAutoEnableIAMAuth` permissions if IAM authentication is already enabled on your databases.

The `rds-db:connect` permission is required to connect to databases. You can reduce the scope of the permission to only allow specific databases, regions, or users. The resource ARN has the following format:

```
arn:aws:rds-db:{Region}:{AccountID}:dbuser:{ResourceID}/{UserName}
```

Refer to [Creating and using an IAM policy for IAM database access](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.IAMPolicy.html) for more information about the `rds-db:connect` permission grant syntax.

Databases discovered by the Teleport Discovery Service should be registered with complete metadata, so you can also omit the `RDSFetchMetadata` permissions if all of your AWS databases are being auto-discovered.

**RDS Proxy**

```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "RDSProxyConnect",
            "Effect": "Allow",
            "Action": "rds-db:connect",
            "Resource": "*"
        },
        {
            "Sid": "RDSProxyFetchMetadata",
            "Effect": "Allow",
            "Action": [
                "rds:DescribeDBProxies",
                "rds:DescribeDBProxyEndpoints"
            ],
            "Resource": "*"
        }
    ]
}

```

| Statement               | Purpose                                                                                                         |
| ----------------------- | --------------------------------------------------------------------------------------------------------------- |
| `RDSProxyConnect`       | Generate an IAM authentication token to connect to a database.                                                  |
| `RDSProxyFetchMetadata` | Automatically import AWS tags as database labels or find missing information such as the database's AWS region. |

The `rds-db:connect` permission is required to connect to databases. You can reduce the scope of the permission to only allow specific databases, regions, or users. The resource ARN has the following format:

```
arn:aws:rds-db:{Region}:{AccountID}:dbuser:{ResourceID}/{UserName}
```

Refer to [Creating and using an IAM policy for IAM database access](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.IAMPolicy.html) for more information about the `rds-db:connect` permission grant syntax.

Databases discovered by the Teleport Discovery Service should be registered with complete metadata, so you can also omit the `RDSProxyFetchMetadata` permissions if all of your AWS databases are being auto-discovered.

**Redshift**

Assign aws-account-id to your AWS account ID:

```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "RedshiftConnectAsDBUser",
            "Effect": "Allow",
            "Action": "redshift:GetClusterCredentials",
            "Resource": "*"
        },
        {
            "Sid": "RedshiftConnectAsIAMRole",
            "Effect": "Allow",
            "Action": "sts:AssumeRole",
            "Resource": [
                "arn:aws:iam::aws-account-id:role/DatabaseUserRole"
            ]
        },
        {
            "Sid": "RedshiftFetchMetadata",
            "Effect": "Allow",
            "Action": "redshift:DescribeClusters",
            "Resource": "*"
        }
    ]
}

```

| Statement                  | Purpose                                                                                                                    |
| -------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `RedshiftConnectAsDBUser`  | Connect to a database as an existing database user.                                                                        |
| `RedshiftConnectAsIAMRole` | Assume an IAM role to connect to a database with permissions mapped into the database 1:1 from the role's IAM permissions. |
| `RedshiftFetchMetadata`    | Automatically import AWS tags as database labels or find missing information such as the database's AWS region.            |

You can reduce the scope of the `RedshiftConnectAsDBUser` statement by updating it to only allow specific users, databases, and database groups. The resource ARN you can specify has the following formats:

```
arn:aws:redshift:{Region}:{AccountID}:dbuser:{ClusterName}/{UserName}
arn:aws:redshift:{Region}:{AccountID}:dbname:{ClusterName}/{DatabaseName}
arn:aws:redshift:{Region}:{AccountID}:dbgroup:{ClusterName}/{DatabaseGroupName}
```

See [Create an IAM role or user with permissions to call GetClusterCredentials](https://docs.aws.amazon.com/redshift/latest/mgmt/generating-iam-credentials-role-permissions.html) for more information about the `redshift:GetClusterCredentials` permission grant syntax.

You can authenticate as an existing database user or as an IAM role that will be automatically mapped into the database. The corresponding IAM statement is only required for the method(s) you want to use. If an IAM role names the Database Service's identity as a trusted principal, and both identities are in the same AWS account, then the `RedshiftConnectAsIAMRole` statement can also be omitted.

Databases discovered by the Teleport Discovery Service should be registered with complete metadata, so you can also omit the `RedshiftFetchMetadata` permissions if all of your AWS databases are being auto-discovered.

**Redshift Serverless**

Assign aws-account-id to your AWS account ID:

```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "RedshiftServerlessConnectAsIAMRole",
            "Effect": "Allow",
            "Action": "sts:AssumeRole",
            "Resource": [
                "arn:aws:iam::aws-account-id:role/DatabaseUserRole"
            ]
        },
        {
            "Sid": "RedshiftServerlessFetchMetadata",
            "Effect": "Allow",
            "Action": [
                "redshift-serverless:GetEndpointAccess",
                "redshift-serverless:GetWorkgroup"
            ],
            "Resource": "*"
        }
    ]
}

```

| Statement                            | Purpose                                                                                                         |
| ------------------------------------ | --------------------------------------------------------------------------------------------------------------- |
| `RedshiftServerlessFetchMetadata`    | Automatically import AWS tags as database labels or find missing information such as the database's AWS region. |
| `RedshiftServerlessConnectAsIAMRole` | Assume an IAM role to connect as a database user.                                                               |

Databases discovered by the Teleport Discovery Service should be registered with complete metadata, so you can also omit the `RedshiftServerlessFetchMetadata` permissions if all of your AWS databases are being auto-discovered.

Redshift Serverless maps IAM roles to database users. The Teleport Database Service must be able to assume these "access" IAM roles which are granted IAM permissions to generate IAM authentication tokens.

## External AWS IAM trust policy

Modify the external AWS IAM role's trust policy to allow the Teleport Database Service's AWS IAM identity as a trusted principal. If you require an external ID, provide a condition in the statement that allows the action only when the correct external ID is given.

For example, if the Teleport Database Service will be deployed to an EC2 instance with an attached role `teleport-db-service` in AWS account `123456789012`, and you want to require an external ID to assume the external role, then the trust policy might look like:

```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Statement1",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::123456789012:role/teleport-db-service"
            },
            "Action": "sts:AssumeRole",
            "Condition": {
                "StringEquals": {
                    "sts:ExternalId": "example-external-id"
                }
            }
        }
    ]
}

```

## Next steps

- Get started by [connecting](https://goteleport.com/docs/enroll-resources/database-access/guides.md) your database.
