gravitational / teleport

The easiest, and most secure way to access and protect all of your infrastructure.
https://goteleport.com
GNU Affero General Public License v3.0
17.55k stars 1.75k forks source link

Database Access - Add support for Session Tags in STS Assume Role API call #33131

Closed smallinsky closed 10 months ago

smallinsky commented 1 year ago

What

In case of handing many AWS DB Resource (like DynamoDB table) a separate IAM role needs to be created with proper hardcoded IAM permission. That lead to following problems:

IAM Role Management: Separate IAM roles need to be created and managed for different AWS database resources (like various DynamoDB tables). Each role should ideally follow the principle of least privilege (POLP). Scalability Concern: The creation and management of multiple IAM roles can lead to configuration overhead and might be perceived as not very scalable, especially in environments with numerous AWS resources.

Proposed Solution:

Using Session Tags in STS Assume Role API: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "dynamodb:Scan",
      "Resource": "arn:aws:dynamodb:::table/$${aws:PrincipalTag/ddb_table_name}"
    }
  ]
}

The above policy allows the entity assuming the role to access the DynamoDB table specified by the TableID session tag dynamically, thus potentially reducing the number of IAM roles required.

Where AWS session Tags can be managed and defined in Teleport Role definition:

kind: role
metadata:

  id: 111
  name: dynamodb-dev-write
spec:
  allow:
    db_labels:
      env: dev
    db_names:
    - '*'
    db_users:
    - teleportDDBWriteRole
    - teleportDDBReadRole
    request: {}
    review_requests: {}
  deny: {}
  options:
    aws:
      session_tags: [ { 'ddb_table_name' : "{{internal.db_name}}" } ]
    cert_format: standard

Example Code Snippet (in Go, using the AWS SDK):

awsConfig.Credentials = aws.NewCredentialsCache(
        stscreds.NewAssumeRoleProvider(sts.NewFromConfig(awsConfig), roleARN, func(options *stscreds.AssumeRoleOptions) {
            options.Tags = tags
        }),
    )
dbmurphy commented 1 year ago

I have been giving a thought to this we might want to use the following versus options.aws.session_tags, so when creating a given DB object we are providing the name, region, account, and any sessionTags needed right?

               {
                    "kind": "db",
                    "version": "v4",
                    "metadata": {
                        "name": dynamo_table_name.replace('_', '-'),
                        "labels": table_info['tags']
                    },
                    "spec": {
                        "protocol": "dynamodb",
                        "aws": {
                            "region": table_info['region'],
                            "account_id": table_info['account_id'],
                            "session_tags": [
                                  { "Name":  "ddb_table_name" , "Value": dynamo_table_name.replace('_', '-') }
                             ]
                        },
                    }
                }

Either way works but I was not sure if it makes more sense to define the tag using internal.db_name at the Role resource level, or if we would want to define tags at the DB resource level.