AWS Security Blog

How to relate IAM role activity to corporate identity

September 8, 2021: The post was updated to correct a typo about the CloudTrail log snippet.

April 14, 2021: In the section “Use the SourceIdentity attribute with identity federation,” we updated “AWS SSO” to “sign-in endpoint” for clarity.


AWS Security Token Service (AWS STS) now offers customers the ability to specify a unique identity attribute for their workforce identities and applications when they assume an AWS Identity and Access Management (IAM) role. This new SourceIdentity attribute makes it easier for you, as an Amazon Web Services (AWS) administrator, to determine the identity that performed the actions while the role was assumed. In this post, I’ll show you how to use the SourceIdentity attribute and how it can help you track usage of your APIs.

The source identity capability means that when you view AWS CloudTrail logs, you can more quickly see which identity is responsible for performing a particular action. For example, you can request that all workforce identities in your company set their user name as the source identity when they assume an IAM role. CloudTrail will log activity that is performed by that role, and when you view logs, you can rely on the user name to identify who is responsible for the action taken while the role was assumed. AWS also persists the source identity information if the workforce identity then assumes a different IAM role during the same session. Assuming a second role while still in the first role’s session is called role chaining.

Previously, AWS administrators were required to set up a complex infrastructure to identify who was responsible for API actions performed while assuming an IAM role. This was difficult to manage and maintain, because it required multiple AWS Lambda functions, Amazon Simple Notification Service (Amazon SNS) topics, and an Amazon DynamoDB database to house and compare IAM access keys across many CloudTrail logs. This was the only method available to determine which identity assumed an IAM role and performed actions in an account. With the new SourceIdentity attribute, administrators can now either require developers to define a source identity when they assume an IAM role, or to specify in their identity provider (IdP) to be included when federating. And this attribute is found in most CloudTrail logs, which makes determining the original identity that assumed the role that much easier.

Use the SourceIdentity attribute

In order for the workforce identity or application to be able to define their source identity when they assume IAM roles, you must first grant them permission for the sts:SetSourceIdentity action. This will permit the workforce identity or application to set the source identity themselves without need for manual intervention. In addition to this permission, the AWS administrator can also add a condition statement for the sts:SourceIdentity key to a role’s trust policy, that would require the workforce identity to set their source identity when they assume the role. By setting this condition as a requirement, you enforce the setting of the SourceIdentity attribute, which allows for easier identification through CloudTrail logs. Let’s see how this new attribute can be used in a few different use cases.

Use the SourceIdentity attribute with identity federation

With the creation of the SourceIdentity attribute and condition, there also comes a parameter that can be passed when federation is used to assume a role. When you use the source identity attribute, the role trust policies for all roles that are connected to the IdP must have the sts:SetSourceIdentity permission. The AssumeRole operation will fail for any role connected to an IdP that is passing the source identity attribute without this permission. When you use IdPs to grant workforce identities access to AWS resources, you can define the source identity in the SAML Response XML file. The SAML Response XML file is then used to create an AWS session for the workforce identity to request access to AWS resources. To define the source identity in the SAML Response XML file, you must add the following snippet to the SAML assertion.

<Attribute Name="https://aws.amazon.com/SAML/Attributes/SourceIdentity">
   <AttributeValue>{UserNameDetail}</AttributeValue>
</Attribute>

Make sure to replace {UserNameDetail} with the value that matches your use case. This could be an email address, user identity, or other value. Any identifying string can be used for this field. After the user authenticates to their IdP, a SAML assertion is returned that contains the user’s information, including the source identity set from the LDAP attribute you specified. This information is sent to the sign-in endpoint, and in return the source identity that was passed is set as the value for the source identity parameter when the end user assumes the role.

{
"sub": "johndoe",
"aud": "ac_oic_client",
"jti": "ZYUCeRMQVtqHypVPWAN3VB",
"iss": "https://example.com",
"iat": 1566583294,
"exp": 1566583354,
"auth_time": 1566583292,
"https://aws.amazon.com/source_identity": "{UserNameDetail}"
}

After the role is assumed and the source identity set, the source identity cannot be changed for the duration of the assume role session. Even if the principal then assumes another role during the same session, the source identity will carry over to the new role. In the event that the new role doesn’t have the sts:SetSourceIdentity permission, you will see an Access Denied error.

This process makes it convenient for you to identify source identity across role-chaining activities. Take a look at the following example CloudTrail event snippet. The SourceIdentity field is supplied in the event, which means that you can identify the principal making the API calls.

"requestParameters": {
        "roleArn": "arn:aws:iam::111122223333:role/Developer_Role",
        "roleSessionName": "Session_Name",
        "sourceIdentity": "Admin"
    },

This same information will be included in every API call that is made, even if the role assumes another role during the same session.

Assume an IAM role

The SourceIdentity attribute can also be used with other types of role assumption. When you use any of the three Assume Role API operations (AssumeRole, AssumeRoleWithSAML, or AssumeRoleWithWebIdentity), you set a value for the –source-identity request parameter. Let’s look at a policy that’s associated to a user and go over each section.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AssumeRole",
            "Effect": "Allow",
            "Action": "sts:AssumeRole",
            "Resource": "arn:aws:iam::<AccountId>:role/Developer_Role"
        },
        {
            "Sid": "Set_AWSUserName_as_SourceIdentity",
            "Effect": "Allow",
            "Action": "sts:SetSourceIdentity",
            "Resource": "arn:aws:iam::<AccountId>:role/Developer_Role",
            "Condition": {
                "StringLike": {
                    "sts:SourceIdentity": "${aws:username}"
                }
            }
        }
    ]
}

The first statement in the policy gives this user permission to call the sts:AssumeRole API and assume the Developer_Role IAM role. (You will need to replace the <AccountId> placeholder with your actual AWS account ID for the policy to be valid.)

{
            "Sid": "AssumeRole",
            "Effect": "Allow",
            "Action": "sts:AssumeRole",
            "Resource": "arn:aws:iam::<AccountId>:role/Developer_Role"
        }

The second portion of the policy is the most interesting. You’re granting permission for the sts:SetSourceIdentity API, as well as requiring it as a condition to complete the assume role request. In this case, the condition states that the source identity must be set as the IAM user name in order to complete the request. If the user name isn’t passed as the source identity, the request to assume the Developer_Role role is denied.

{
            "Sid": "Set_AWSUserName_as_SourceIdentity",
            "Effect": "Allow",
            "Action": "sts:SetSourceIdentity",
            "Resource": "arn:aws:iam::<AccountId>:role/Developer_Role",
            "Condition": {
                "StringLike": {
                    "sts:SourceIdentity": "${aws:username}"
                }
            }
        }

The condition can require any string value of your choosing. This includes exact values such as an AWS user name or a predefined list of values. You can also set this condition in the trust policy of an IAM role, thus requiring the source identity from any principal that wants to assume the role. The command line interface (CLI) command to pass the SourceIdentity parameter when assuming a role would look similar to the following example.

aws sts assume-role --role-arn arn:aws:iam::<AccountId>:role/Developer_Role --role-session-name Audit --source-identity Admin

Identifying the API activity across this identity works just like it did for federation. Simply follow the source identity across the CloudTrail logs to see what API calls were performed. In the following example, you can see a CloudTrail log snippet after a role was assumed through the AssumeRole API.

"requestParameters": {
        "roleArn": "arn:aws:iam::111122223333:role/Developer_Role",
        "roleSessionName": "Session_Name",
        "sourceIdentity": "Admin"
    },

Put the SourceIdentity attribute to work

Now that I’ve shown you how the new SourceIdentity attribute works, let’s follow it through some API calls to see how easy identifying the source identity will be. In the first API call, you’ll perform the AssumeRole API operation to assume the Developer_Role.

{
    "eventVersion": "1.08",
    "userIdentity": {
        "type": "IAMUser",
        "principalId": "AIDACKCEVSQ6C2EXAMPLE",
        "arn": "arn:aws:iam::111122223333:user/user_1",
        "accountId": "111122223333",
        "accessKeyId": "AKIAIOSFODNN7EXAMPLE",
        "userName": "user_1"
    },
    "eventTime": "2021-01-21T23:46:28Z",
    "eventSource": "sts.amazonaws.com",
    "eventName": "AssumeRole",
    "awsRegion": "global",
    "sourceIPAddress": "AWS Internal",
    "userAgent": "aws-cli/1.18.216 Python/3.6.12 Linux/4.9.230-0.1.ac.223.84.332.metal1.x86_64 botocore/1.19.56",
    "requestParameters": {
        "roleArn": "arn:aws:iam::111122223333:role/Developer_Role",
        "roleSessionName": "Session_Name",
        "sourceIdentity": "Admin"
    },
    "responseElements": {
        "credentials": {
            "accessKeyId": "ASIAIOSFODNN7EXAMPLE",
            "expiration": "Jan 22, 2021 12:46:28 AM",
            "sessionToken": "FwoGZXIvYXdzEDcaDJzPCaIgEdPDNCpwpiLNAUawkSIVuHDGLtDe1McHzt6lb9bb8aXA01Hu01P7IZz4tRIUWhC0hVCZtZV6vV11AvSy7x+8aCAchIJDSzM033UnJcn06OI7EZlCbM51EFjtdbVysj4ofYkPyQE0aLzz5TIO6YQ9QQI4/4GmZWMVlmnc3nJx9fAMmzcDslgBK2EgaCRN2SNMWdDqE/bd+nFuX/puyWhX1g6B3nCJ59wMaWEptyqWdmVun6YV5L1SDJB03qNj5TigIsKJl99CoP67SDdTm7gs9vOpbYVGEEgo1KSogAYyMt30kteqQ5ONKZ5RsjGhp3e4sMsww0ZLd/nSOEZfXPXvsTMxKoaCXEd7TzL5mGoXtYYY"
        },
        "assumedRoleUser": {
            "assumedRoleId": "AROACKCEVSQ6C2EXAMPLE:Session_Name",
            "arn": "arn:aws:sts::111122223333:assumed-role/Developer_Role/Session_Name"
        },
        "sourceIdentity": "Admin"
    },
    "requestID": "dc4ecf82-c52e-4ac8-bd2f-2f854f017911",
    "eventID": "c632753c-5835-464d-91d6-b2d75dc8d7fc",
    "readOnly": true,
    "resources": [
        {
            "accountId": "111122223333",
            "type": "AWS::IAM::Role",
            "ARN": "arn:aws:iam::111122223333:role/Developer_Role"
        }
    ],
    "eventType": "AwsApiCall",
    "managementEvent": true,
    "recipientAccountId": "111122223333",
    "eventCategory": "Management"
}

Now that you’ve assumed the role and set your source identity attribute, let’s see what another API call log would look like. In this example, I’ll perform the ListRoles API call.

{
    "eventVersion": "1.08",
    "userIdentity": {
        "type": "AssumedRole",
        "principalId": "AROACKCEVSQ6C2EXAMPLE:Session_Name",
        "arn": "arn:aws:sts::111122223333:assumed-role/Developer_Role/Session_Name",
        "accountId": "111122223333",
        "accessKeyId": "ASIAIOSFODNN7EXAMPLE",
        "sessionContext": {
            "sessionIssuer": {
                "type": "Role",
                "principalId": "AROACKCEVSQ6C2EXAMPLE",
                "arn": "arn:aws:iam::111122223333:role/Developer_Role",
                "accountId": "111122223333",
                "userName": "Developer_Role"
            },
            "webIdFederationData": {},
            "attributes": {
                "mfaAuthenticated": "false",
                "creationDate": "2021-01-21T23:46:28Z"
            },
            "sourceIdentity": "Admin"
        }
    },
    "eventTime": "2021-01-21T23:48:09Z",
    "eventSource": "iam.amazonaws.com",
    "eventName": "ListRoles",
    "awsRegion": "us-east-1",
    "sourceIPAddress": "AWS Internal",
    "userAgent": "aws-cli/1.18.216 Python/3.6.12 Linux/4.9.230-0.1.ac.223.84.332.metal1.x86_64 botocore/1.19.56",
    "requestParameters": null,
    "responseElements": null,
    "requestID": "99a23348-0f68-4ced-baab-e4282c320086",
    "eventID": "88a02896-e1a4-4170-8f60-48f8009fc608",
    "readOnly": true,
    "eventType": "AwsApiCall",
    "managementEvent": true,
    "eventCategory": "Management",
    "recipientAccountId": "111122223333"
}

To see how the source identity attribute will follow the session, from this role you can perform the AssumeRole API call on another role, from this same session. You can see that log example following.

{
    "eventVersion": "1.08",
    "userIdentity": {
        "type": "AssumedRole",
        "principalId": "AROACKCEVSQ6C2EXAMPLE:Session_Name",
        "arn": "arn:aws:sts::111122223333:assumed-role/Developer_Role/Session_Name",
        "accountId": "111122223333",
        "accessKeyId": "ASIAIOSFODNN7EXAMPLE",
        "sessionContext": {
            "sessionIssuer": {
                "type": "Role",
                "principalId": "AROACKCEVSQ6C2EXAMPLE",
                "arn": "arn:aws:iam::111122223333:role/Developer_Role",
                "accountId": "111122223333",
                "userName": "Developer_Role"
            },
            "webIdFederationData": {
                
            },
            "attributes": {
                "mfaAuthenticated": "false",
                "creationDate": "2021-01-21T23:46:28Z"
            },
            "sourceIdentity": "Admin"
        }
    },
    "eventTime": "2021-01-21T23:49:38Z",
    "eventSource": "sts.amazonaws.com",
    "eventName": "AssumeRole",
    "awsRegion": "us-east-1",
    "sourceIPAddress": "AWS Internal",
    "userAgent": "aws-cli/1.18.216 Python/3.6.12 Linux/4.9.230-0.1.ac.223.84.332.metal1.x86_64 botocore/1.19.56",
    "requestParameters": {
        "roleArn": "arn:aws:iam::444455556666:role/Developer_Role_Admin",
        "roleSessionName": "Session_Name_Two",
        "sourceIdentity": "Admin"
    }

And one final log example to showcase the further session continuance of the source identity attribute—in the following example, you can see another API call log that uses the second role that is assumed.

{
    "eventVersion": "1.08",
    "userIdentity": {
        "type": "AssumedRole",
        "principalId": "AROACKCEVSQ6C2EXAMPLE:Session_Name_Two",
        "arn": "arn:aws:sts::444455556666:assumed-role/Developer_Role_Admin/Session_Name_Two",
        "accountId": "444455556666",
        "accessKeyId": "ASIAIOSFODNN7EXAMPLE",
        "sessionContext": {
            "sessionIssuer": {
                "type": "Role",
                "principalId": "AROAUYE7YFMOUKDGPBVHZ",
                "arn": "arn:aws:iam::444455556666:role/Developer_Role_Admin",
                "accountId": "444455556666",
                "userName": "Developer_Role_Admin"
            },
            "webIdFederationData": {},
            "attributes": {
                "mfaAuthenticated": "false",
                "creationDate": "2021-01-21T23:49:38Z"
            },
            "sourceIdentity": "Admin"
        }
    },
    "eventTime": "2021-01-21T23:51:52Z",
    "eventSource": "iam.amazonaws.com",
    "eventName": "ListPolicies",
    "awsRegion": "us-east-1",
    "sourceIPAddress": "AWS Internal",
    "userAgent": "aws-cli/1.18.216 Python/3.6.12 Linux/4.9.230-0.1.ac.223.84.332.metal1.x86_64 botocore/1.19.56",
    "requestParameters": {
        "onlyAttached": false,
        "marker": "AAIY4mMzWCZE8V8dhsZEnIHXBxUGgrWryA010zyDtHD/XY0PVi3eIjDnN9YA3KYGgGwrD2SGquWm01a0vOqaIpot/n7KcDZppXPqU4TDflTyVA=="
    },
    "responseElements": null,
    "requestID": "5560e442-6c3b-431a-bf7e-9159a2d51f23",
    "eventID": "7b89c2b1-ff59-44d1-86ac-bc4487cd7b0a",
    "readOnly": true,
    "eventType": "AwsApiCall",
    "managementEvent": true,
    "eventCategory": "Management",
    "recipientAccountId": "444455556666"
}

Conclusion

In this post, I introduced you to the new AWS STS SourceIdentity attribute. I showed you how to set up the attribute with federation to AWS resources, as well as through API calls like AssumeRole, AssumeRoleWithSAML, and AssumeRoleWithWebIdentity. I supplied CloudTrail logs to show how the source identity will appear in logs and provided multiple logs over one session to demonstrate the continuance of the source identity attribute. You can now use this attribute to more easily identify identity and API usage across role sessions. Test it out for yourself by using the CLI to set the SourceIdentity when assuming an IAM role.

If you have feedback about this post, submit comments in the Comments section below. If you have questions about this post, start a new thread on the AWS IAM forum or contact AWS Support.

Want more AWS Security how-to content, news, and feature announcements? Follow us on Twitter.

Author

Tracy Pierce

Tracy Pierce is a Senior Security Consultant for Engagement Security. She enjoys the peculiar culture of Amazon and uses that to ensure that every day is exciting for her fellow engineers and customers alike. Customer obsession is her highest priority both internally and externally. She has her AS in Computer Security and Forensics from Sullivan College of Technology and Design, Systems Security Certified Practitioner (SSCP) certification, AWS Developer Associate certification, AWS Solutions Architect Associates certificate, and AWS Security Specialist certification. Outside of work, she enjoys time with friends, her fiancé, her Great Dane, and three cats. She also reads (a lot), builds Legos, and loves glitter.