RDS IAM Access for Individual Duplo Users
Edit This is a way to give individual Duplo users passwordless access to their own RDS user via their Duplo JIT access. It integrates RDS IAM authentication into Duplo's access control. The first half of the doc is a method that works with today's Duplo. The second is a discussion of simpler approach that would be possible with platform changes. Things to watch out for when working with this: The eventual consistency of IAM policy changes was consistently 5+ minutes. If you don't get the results you expect, wait a while and retry. Your Duplo JIT cache (which is different for duplo-jit and duploctl ) can end up giving you unexpected access if it hasn't expired yet. You may need to clear it to get correct results. Things to know before reading: RDS IAM authentication is currently a feature flag in Duplo . This page assumes you've set that flag and then enabled IAM authentication for your database. Databases can have tight name length restrictions. 32 characters in the use-case this was built around. When Duplo does JIT to AWS for users, it sets a consistent IAM role session name that includes both the Duplo username and the tenant IAM role name: [email protected] The opening section of the AWS doc on role session name and source identity . Where this doesn't work well: Readonly Duplo users. Initially wouldn't work at all because readonly users can't assume any role. A new feature was on the way that would enable customizing readonly access so it could assume a role, but readonly users would still be able to get the RDS admin password and secrets that may contain more RDS access from the Duplo UI. Maybe after the new RBAC feature comes out we could lock this down, but if you need a genuinely "readonly" RDS user password access may be the better choice. Use an Intermediate IAM Role You can do this today. It works with Duplo's existing tenant role and JIT. This is written as an example using the [email protected] user and a MySQL Aurora cluster in the qa01 tenant. Create a User-Specific IAM Role For this example we'll call it [email protected] . Give it a trust policy that allows it to be assumed by sessions of the tenant role that match that user's DuploCloud name: { "Version": "2012-10-17", "Statement": [ { "Sid": "AllowOneUserToAssumeThisRole", "Effect": "Allow", "Principal": { "AWS": "arn:aws-us-gov:iam::123456789012:role/duploservices-qa01" }, "Action": "sts:AssumeRole", "Condition": { "StringLike": { "aws:userid": "AIDAJQABLZS4A3QDU576Q:[email protected]" } } } ] } (The AIDAJQABLZS4A3QDU576Q string is the unique id of the tenant role.) When Duplo users assume the tenant role through JIT, the aws:userid IAM condition key will contain the role session name that was set by Duplo . That session name contains the Duplo username. The trust policy above uses this to set a condition that only allows one user to assume the role. There's a note here that the role session name can be changed. Since the session name is where we match on the Duplo username, this could be used to let one user assume another user's database role. The only way to change it that was identified during testing was to re-assume the role with a new session name. The tenant role can't assume itself, so this didn't allow users to assume each other's database roles. This trust policy is the only Duplo-specific piece. The rest of the steps in this section are just an example of the RDS IAM authentication instructions. They're included to make this easy to test. Give that role permission to get IAM auth credentials : { "Version": "2012-10-17", "Statement": [ { "Action": "rds-db:connect", "Effect": "Allow", "Resource": "arn:aws-us-gov:rds-db:us-gov-west-1:123456789012:dbuser:cluster-2AA54GLEY3YODPA4OTCRYT3OEM/[email protected]", "Sid": "AllowUserToAuthToMySQL" } ] } ( dbuser above is an AWS-controlled ARN component. The actual database username comes from the [email protected] at the end.) You could use this policy to give the user access to a database user with any name. It uses the Duplo username because it's convenient. Give the Tenant Role Permission to Assume the New Role { "Version": "2012-10-17", "Statement": [ { "Action": [ "sts:AssumeRole" ], "Effect": "Allow", "Resource": "arn:aws-us-gov:rds-db:us-gov-west-1:123456789012:role/[email protected]", "Sid": "AllowTenantToAssumeMySQLUserRole" } ] } If you're setting this up for many users, you can wildcard the username to limit the number of policy statements you have to add. This won't allow users to assume each other's roles because the trust policy of each role still matches on the username. { "Version": "2012-10-17", "Statement": [ { "Action": [ "sts:AssumeRole" ], "Effect": "Allow", "Resource": "arn:aws-us-gov:rds-db:us-gov-west-1:123456789012:role/qa01-mysql-*", "Sid": "AllowTenantToAssumeMySQLUserRole" } ] } Create a Database User Like this for MySQL: CREATE USER IF NOT EXISTS '[email protected]' IDENTIFIED WITH AWSAuthenticationPlugin AS 'RDS'; This username must match the one in the permissions you gave the [email protected] role. Connect Assume the [email protected] role and then use instructions like these . That's pretty standard AWS and database scripting, so it's omitted to keep this article from getting too long. Some teams use tools like JetBrains DataGrip that can be setup for IAM auth directly. Those tools may not support assuming an intermediate role, but you can use the ~/.aws/config to handle that step: [profile datagrip] role_arn = arn:aws-us-gov:iam::123456789012:role/[email protected] source_profile = [NAME OF YOUR NON-ADMIN DUPLO PROFILE] Then configure DataGrip (or whatever tool) to use the datagrip profile and the AWS SDK that it uses behind the scenes will automatically assume the user's role before DataGrip tries to generate RDS credentials. Use AssumeRole's Source Identity This is a discussion of a simpler pattern that was found while testing the pattern above. It would require changes to DuploCloud. This pattern was tested using scripts and manually created roles that simulated what Duplo would need to do. When assuming roles, you can optionally set a source identity . It's separate from the role session name. It has an associated aws:SourceIdentity IAM condition key . This is a global condition key, so it can be used as a policy variable . That means it can be used in more than just conditions, you can interpolate it in resource paths in IAM policies. If we expanded Duplo JIT to set the source identity to a user-specific value (like their username), we could give the tenant role a policy like this: { "Version": "2012-10-17", "Statement": [ { "Sid": "MapSourceIdentityToDatabaseUser", "Effect": "Allow", "Action": [ "rds-db:connect" ], "Resource": [ "arn:aws-us-gov:rds-db:us-gov-west-1:123456789012:dbuser:cluster-2AA54GLEY3YODPA4OTCRYT3OEM/${aws:SourceIdentity}" ] } ] } ( dbuser above is an AWS-controlled ARN component. The actual database username comes from the ${aws:SourceIdentity} at the end.) Then, when users JIT to the tenant role, they would have permission to get database credentials for a database user with a name that matches the source identity that Duplo set. As long as that user existed in the database, they could authenticate. They wouldn't be able to authenticate as another user because they couldn't change the source identity in their assume role call because Duplo assumes the role for them. This may also be easier to keep secure because the source identity can't be changed after it's set . One thing to consider before using this pattern is that source identity is generic beyond just RDS IAM authentication. Maybe it would have other uses. We should be careful about locking ourselves in to using it just for this.
Last updated
Was this helpful?

