AWS Cross-Account Role Assumption Denied Access for STS Role Session

47 views Asked by At

I have account-A that a service uses a role session arn:aws:sts::{account-A}:assumed-role/Worker..., which needs to access Secrets Manager in account-B.

In account-B, I have define a secret manager role which has a policy that give access to secret manager.

Account-A's worker gets access deny when assuming the role.

// Function that gets assumed role in account-B, in context of arn:aws:sts::{account-A}:assumed-role/Worker{hashID}
async function assumeCrossAccountRole(accountId: string) {
  const sts = new STS();
  const RoleArn = `arn:aws:iam::${accountId}:role/SecretsManagerRole`; // account-B
  const testAWS = await sts
    .assumeRole({ RoleArn, RoleSessionName: 'CrossAccountSession' })
    .promise();
  const credentials: CredentialsOptions = {
    accessKeyId: testAWS.Credentials.AccessKeyId,
    secretAccessKey: testAWS.Credentials.SecretAccessKey,
    sessionToken: testAWS.Credentials.SessionToken,
  };
}

// Trying to get Secrets Manager with the assumed role
const credentials = await assumeCrossAccountRole(config.env.account);
const secretsManager = new SecretsManager({ region, credentials });

Here is the trust policy I'm trying out. In the principal statement, I cannot just put the Worker Arn because the Workers are system generated with hash ID, thus I'm using the Condition statement.

// In account-B, trust policy for secrets manager access
{
  "Effect": "Allow",
  "Principal": { "AWS": "*" },
  "Action": ["sts:AssumeRole"],
  "Condition": {
    "ForAnyValue:StringLike": {
      "aws:PrincipalArn": [
        "arn:aws:sts::{account-A}:assumed-role/Worker*",
        "arn:aws:sts::{account-A}:assumed-role/Invoker*"
      ]
    }
  }
}

How can I modify policies such that I can get the STS role session worker to assume role in a different account?


Edit, service worker in account-A has this trust policy:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": { "Service": "ecs-tasks.amazonaws.com" },
            "Action": "sts:AssumeRole"
        },
        {
            "Effect": "Allow",
            "Principal": { "Service": "lambda.amazonaws.com" },
            "Action": "sts:AssumeRole"
        }
    ]
}
1

There are 1 answers

0
Noble Dinasaur On

After some testing. I figure out that this need both policy and trust.

  1. Worker (account-A) mush have sts:AssumeRole permission. (Not in trust policy) as @john-rotenstein pointed in comment. I previously did not have this policy attach to my worker.
{
  Version: '2012-10-17',
  Statement: [
    {
      Sid: 'Statement1',
      Effect: 'Allow',
      Action: ['sts:AssumeRole'],
      Resource: ['*'],
    },
  ],
}
  1. Worker is good to have self IAM assumeRole in the trust relationship. This give more flexibility for testing. But probably not required.
{
  Sid: 'AllowAccounts',
  Effect: 'Allow',
  Principal: { AWS: [arn:aws:iam::${account-A}:root] },
  Action: 'sts:AssumeRole',
}
  1. The Secrets Manager Role (account-B), instead of using STS, should condition with principle account instead of arn.
{
  Version: '2012-10-17',
  Statement: [
    {
      Effect: 'Allow',
      Principal: { AWS: '*' },
      Action: ['sts:AssumeRole', 'sts:TagSession'],
      Condition: {
        'ForAnyValue:StringEquals': {
          'aws:PrincipalAccount': [{account-A}],
        },
      },
    },
  ],
}
  1. Although this was not mentioned in the original question. Here is the policy for the secrets access.
{
    Version: '2012-10-17',
    Statement: [
      {
        Effect: 'Allow',
        Action: ['secretsmanager:GetSecretValue'],
        Resource: `arn:aws:secretsmanager:${account-B-region}:${account-B}:secret:{secret-id}*`,
      },
    ],
  }

I was able to get my worker federation and read the secret value.