Skip to content

AWS OIDC - DeployService: configure IAM#28088

Merged
marcoandredinis merged 3 commits intomasterfrom
marco/integration_configure
Jun 30, 2023
Merged

AWS OIDC - DeployService: configure IAM#28088
marcoandredinis merged 3 commits intomasterfrom
marco/integration_configure

Conversation

@marcoandredinis
Copy link
Copy Markdown
Contributor

@marcoandredinis marcoandredinis commented Jun 20, 2023

DeployService allows the user to deploy a Database Service (other services will be available later on) in a single click.
You can read more about it here:
#27035
#25521 (comment)

The IAM set up consists of:

  • creating a new Role with specific Policies and set a Boundary Policy
  • allowing the Integration role to pass that Role when starting the ECS Service
    It's quite tedious and error prone.

Instead of giving users a detailed guide on how to set this up, we will ask users to run this command on their machine or Amazon CloudShell.
They must have the following permission to be able to run the configuration command:

- iam:CreatePolicy
- iam:CreateRole
- iam:PutRolePolicy
- iam:TagPolicy
- iam:TagRole

The script does not try to be smart:

  • only creates the resources if they don't exist previously
  • does not attempt to rollback any created resources if some action fails

We can add more checks to ensure a better UX, but we decided to keep things simple and safe for now.
If you think otherwise, please let me know.

Demo:

ubuntu@ip-172-31-39-120 ~/pg [1]> teleport integration configure deployservice-iam --cluster lenix --name teleportdev --aws-region eu-west-1 --role MarcoTestRoleOIDCProvider --task-role MarcoRoleForDS
2023/06/26 16:21:51 TaskRole: Boundary Policy "MarcoRoleForDSBoundary" created.
2023/06/26 16:21:51 TaskRole: Role "MarcoRoleForDS" created with Boundary "arn:aws:iam::278576220453:policy/MarcoRoleForDSBoundary".
2023/06/26 16:21:51 TaskRole: IAM Policy "MarcoRoleForDS" added to Role "MarcoRoleForDS".
2023/06/26 16:21:51 IntegrationRole: IAM Policy "DeployService" added to Role "MarcoTestRoleOIDCProvider"

Running the script again, returns an error indicating that the policy (used as Task Role Boundary) already exists

ubuntu@ip-172-31-39-120 ~/pg> teleport integration configure deployservice-iam --cluster lenix --name teleportdev --aws-region eu-west-1 --role MarcoTestRoleOIDCProvider --task-role MarcoRoleForDS
ERROR: Policy "MarcoRoleForDSBoundary" already exists, please remove it and try again.

If the IntegrationRole does not exist:

ubuntu@ip-172-31-39-120 ~/pg [1]> teleport integration configure deployservice-iam --cluster lenix --name teleportdev --aws-region eu-west-1 --role RoleThatDoesNotExist --task-role MarcoRoleForDS
2023/06/26 16:23:55 TaskRole: Boundary Policy "MarcoRoleForDSBoundary" created.
2023/06/26 16:23:55 TaskRole: Role "MarcoRoleForDS" created with Boundary "arn:aws:iam::278576220453:policy/MarcoRoleForDSBoundary".
2023/06/26 16:23:55 TaskRole: IAM Policy "MarcoRoleForDS" added to Role "MarcoRoleForDS".
ERROR: Role "RoleThatDoesNotExist" not found.

@marcoandredinis marcoandredinis marked this pull request as draft June 20, 2023 21:59
@github-actions github-actions Bot requested review from r0mant and rudream June 20, 2023 21:59
@marcoandredinis marcoandredinis force-pushed the marco/integration_configure branch 7 times, most recently from 264fdef to 3c0d30d Compare June 23, 2023 17:04
@marcoandredinis marcoandredinis force-pushed the marco/integration_configure branch 3 times, most recently from a57099f to 1ca17fd Compare June 26, 2023 17:19
@marcoandredinis marcoandredinis marked this pull request as ready for review June 26, 2023 17:29
@marcoandredinis marcoandredinis force-pushed the marco/integration_configure branch from 1ca17fd to 4ca207d Compare June 27, 2023 07:41
Copy link
Copy Markdown
Contributor

@smallinsky smallinsky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First pass.

Comment thread tool/teleport/common/teleport.go Outdated
Comment thread lib/integrations/awsoidc/tags.go Outdated
Comment thread lib/cloud/aws/errors_test.go Outdated
Comment thread lib/cloud/aws/policy.go
Resources SliceOrString `json:"Resource"`
Resources SliceOrString `json:"Resource,omitempty"`
// Principals is a list of principals.
Principals map[string]SliceOrString `json:"Principal,omitempty"`
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Principals map[string]SliceOrString `json:"Principal,omitempty"`
Principal map[string]SliceOrString `json:"Principal,omitempty"`

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was trying to be consistent with the fields above
They use plural form but then the json tag is singular
Should we change them to singular as well? Resource and Action vs Resources and Actions

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I haven't notice this. Lets ignore my comment and keep this constant.

Comment thread tool/teleport/common/teleport.go Outdated
Comment thread tool/teleport/common/teleport.go Outdated
Comment thread lib/config/configuration.go Outdated
Comment thread lib/integrations/awsoidc/deployservice_iam_config.go Outdated
Comment thread lib/integrations/awsoidc/deployservice_iam_config.go Outdated

joinOpenSSH.Flag("debug", "Enable verbose logging to stderr.").Short('d').BoolVar(&ccf.Debug)

integrationCmd := app.Command("integration", "Integration commands")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The integration is quite generic but what is the difference between :
-- teleprot discovery bootstrap
-- db configure bootstrap

Do we need to introduce new patter for the deployservice-iam service.

Can we just create teleport deployservice-iam bootstrap/configure commands ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not (yet) discovery related.
It only installs a DatabaseService in Amazon ECS for the proxy.

We can, yeah
We might have other configuration needs when setting up the IAM requirements for other integrations and I was trying to create an abstraction/layer for them. So that they don't pollute the top level teleport commands

@marcoandredinis
Copy link
Copy Markdown
Contributor Author

Friendly ping @smallinsky @gabrielcorado @greedy52 🙏

Copy link
Copy Markdown
Contributor

@smallinsky smallinsky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some nit comments but mostly LGTM.

Comment thread lib/cloud/aws/policy.go
Resources SliceOrString `json:"Resource"`
Resources SliceOrString `json:"Resource,omitempty"`
// Principals is a list of principals.
Principals map[string]SliceOrString `json:"Principal,omitempty"`
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I haven't notice this. Lets ignore my comment and keep this constant.

Comment thread lib/cloud/aws/policy.go Outdated
Comment thread lib/cloud/aws/policy_statements.go Outdated
Comment thread lib/cloud/aws/policy_statements.go Outdated
func (d awsTags) ToIAMTags() []iamTypes.Tag {
iamTags := make([]iamTypes.Tag, 0, len(d))
for k, v := range d {
k, v := k, v
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Key and Value of d awsTags[map[string]string are "value" types. Why do we need make a second copy ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But then we use their address because iamTypes.Tag.Key/Value require string pointers.
I just removed the second copy and the tests in tags_test.go fail because of it

Copy link
Copy Markdown
Contributor

@greedy52 greedy52 Jun 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think they will fix this in newer go, eventually
https://github.com/golang/go/wiki/LoopvarExperiment.
An alternative is to use aws.String() instead of &k/&v that takes care of the copy through function call

@ravicious ravicious removed their request for review June 30, 2023 13:32
Copy link
Copy Markdown
Contributor

@gabrielcorado gabrielcorado left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

Comment thread lib/cloud/aws/policy.go Outdated
}

// PolicyARN returns the ARN representation of an AWS IAM Policy.
func PolicyARN(partition, accountID, policy string) string {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: maybe a good place to put these functions is lib/utils/aws/aws.go. There are some similar functions, but for the IAM role and user.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

	awsapiutils "github.com/gravitational/teleport/api/utils/aws"
	awslib "github.com/gravitational/teleport/lib/cloud/aws"
	awslibutils "github.com/gravitational/teleport/lib/utils/aws"

🥵

}

// NewDeployServiceIAMConfigureClient creates a new DeployServiceIAMConfigureClient.
func NewDeployServiceIAMConfigureClient(ctx context.Context, region string) (DeployServiceIAMConfigureClient, error) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A question that might be outside this PR scope: What about using the already existing cloud clients for getting AWS clients? It already initializes the connection (I know the db configurator does not rely on it too, but there is no reason not to use it). The good thing is that AWS already supports some good options, like cross-account clients, and already has a test implementation. That would also help us get everything to use the same SDK major version.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My 2c on this, we should really move to v2. I submitted a small request in v1 a few months ago. They rejected and ask me to request it in v2. Maybe we should start a cloud.ClientsV2.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I really wanted to use V2 here
I could create the same abstraction for V2, but that seemed to be overkill for this PR

Comment thread lib/cloud/aws/policy.go Outdated
Comment on lines +93 to +100
// StatementForRDSDBConnect returns a statement that allows the `rds-db:connect` for all RDS DBs.
func StatementForRDSDBConnect() *Statement {
return &Statement{
Effect: EffectAllow,
Actions: SliceOrString{"rds-db:connect"},
Resources: allResources,
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there plan to support other DB types of deploy service? if so, ideally can share this with the db actions listed in configurators/aws. I guess we can refactor when that days comes too =D.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will probably have more in the future 👍

There's indeed some duplication that we should get rid off.
Let's see how the DeployService evolves and then centralize all these statements.

if trace.IsAlreadyExists(awslib.ConvertIAMv2Error(err)) {
return trace.AlreadyExists("Role %q already exists, please remove it and try again.", req.TaskRole)
}
return trace.Wrap(err)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should here return the converted type?

Copy link
Copy Markdown
Contributor Author

@marcoandredinis marcoandredinis Jun 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to change the message so that it includes an action to resolve the issue ("remove it").
This way, the user can fix it.

If I return the converted type, then it will be the raw AWS error message.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i meant the return trace.Wrap(err) after the already exist check (like return trace.Wrap(the-converted-error). I guess not big difference.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh right 🙏
Yes! changing

Comment thread lib/integrations/awsoidc/tags_test.go Outdated
}

_, err = clt.PutRolePolicy(ctx, &iam.PutRolePolicyInput{
PolicyName: &req.IntegrationRoleDeployServicePolicy,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can an intergration role used for multiple task roles? if so, the policy names will collide.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might need to change this when we have multiple Deployment Modes.
For now, there's only database-service so they should not collide.

I'll make sure to add a suffix when we add a policy for another deployment.
Or we might have this policy always having the set of required statements for the deployments we have

Comment thread lib/cloud/aws/policy_statements.go Outdated
Comment thread lib/integrations/awsoidc/deployservice_iam_config.go
Comment thread lib/integrations/awsoidc/deployservice_iam_config.go
@marcoandredinis marcoandredinis force-pushed the marco/integration_configure branch from 79ed5c9 to 279b9c6 Compare June 30, 2023 18:16
@public-teleport-github-review-bot public-teleport-github-review-bot Bot removed the request for review from GavinFrazar June 30, 2023 18:17
@marcoandredinis marcoandredinis added discover Issues related to Teleport Discover backport/branch/v13 labels Jun 30, 2023
@marcoandredinis marcoandredinis added this pull request to the merge queue Jun 30, 2023
Merged via the queue into master with commit e043ac0 Jun 30, 2023
@marcoandredinis marcoandredinis deleted the marco/integration_configure branch June 30, 2023 18:53
@public-teleport-github-review-bot
Copy link
Copy Markdown

@marcoandredinis See the table below for backport results.

Branch Result
branch/v13 Failed

marcoandredinis added a commit that referenced this pull request Jul 3, 2023
* auto configure deployservice iam

* review pt1

* review pt2
github-merge-queue Bot pushed a commit that referenced this pull request Jul 3, 2023
* auto configure deployservice iam

* review pt1

* review pt2
@r0mant r0mant mentioned this pull request Jul 14, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

discover Issues related to Teleport Discover size/lg size/md

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants