Repository: 99designs/aws-vault (language: go)

Issue:
Support for Source Identity on sessions?
AWS recently added a new feature to IAM sessions called a "source identity" - it sounds like a "stronger" version of using a session name to identify who assumed a role:

> When you perform actions in AWS, the information about your session can be logged to AWS CloudTrail for your account administrator to monitor. Administrators can configure roles to require identities to pass a custom string that identifies the person or application that is performing actions in AWS. This identity information is stored as the source identity in AWS CloudTrail. When the administrator reviews activity in CloudTrail, they can view the source identity information to determine who or what performed actions with assumed role sessions.
> 
> After a source identity is set, it is present in requests for any AWS action taken during the role session. The value that is set persists when a role is used to assume another role through the AWS CLI or AWS API, known as role chaining. The value that is set cannot be changed during the role session. Administrators can configure granular permissions based on the presence or value of the source identity to further control AWS actions that are taken with shared roles. You can decide whether the source identity attribute can be used, whether it is required, and what value can be used.
> 
> The way that you use source identity differs from role session name and session tags in an important way. The source identity value can't be changed after it is set, and it persists for any additional actions that are taken with the role session. Here's how you can use session tags and role session name:
> 
> *    Session tags – You can pass session tags when you assume a role or federate a user. Session tags are present when a role is assumed. You can define policies that use tag condition keys to grant permissions to your principals based on their tags. Then you can use CloudTrail to view the requests made to assume roles or federate users. To learn more about session tags, see Passing session tags in AWS STS.
> 
> *    Role session name – You can use the sts:RoleSessionName condition key in a role trust policy to require that your users provide a specific session name when they assume a role. Role session name can be used to differentiate role sessions when a role is used by different principals. To learn more about role session name, see sts:RoleSessionName.
> 
> We recommend that you use source identity when you want to control the identity that assumes a role. Source identity is also useful for mining CloudTrail logs to determine who used the role to perform actions. 

References:

* https://aws.amazon.com/about-aws/whats-new/2021/04/aws-identity-and-access-management-now-makes-it-easier-to-relate-a-users-iam-role-activity-to-their-corporate-identity/
* https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_control-access_monitor.html


Discussion / hints:

Fix patch:
diff --git a/USAGE.md b/USAGE.md
index 395ddcf8a..4d2cfdf88 100644
--- a/USAGE.md
+++ b/USAGE.md
@@ -118,6 +118,20 @@ session_tags = key1=value1,key2=value2,key3=value3
 transitive_session_tags = key1,key2
 ```
 
+#### `source_identity`
+
+It is possible to set [source identity](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_control-access_monitor.html) when `AssumeRole` is used. Custom config variable `source_identity` allows you to set the value.
+
+```ini
+[profile root]
+region=eu-west-1
+
+[profile order-dev]
+source_profile = root
+role_arn=arn:aws:iam::123456789:role/developers
+source_identity=your_user_name
+```
+
 
 ### Environment variables
 
@@ -153,6 +167,8 @@ To override or set session tagging (used in `exec`):
 * `AWS_SESSION_TAGS`: Comma separated key-value list of tags passed with the `AssumeRole` call, overrides `session_tags` profile config variable
 * `AWS_TRANSITIVE_TAGS`: Comma separated list of transitive tags passed with the `AssumeRole` call, overrides `transitive_session_tags` profile config variable
 
+To override or set the source identity (used in `exec` and `login`):
+* `AWS_SOURCE_IDENTITY`: Specifies the source identity for assumed role sessions
 
 ## Backends
 
diff --git a/vault/assumeroleprovider.go b/vault/assumeroleprovider.go
index a1e09dbf8..288be4a7a 100644
--- a/vault/assumeroleprovider.go
+++ b/vault/assumeroleprovider.go
@@ -20,6 +20,7 @@ type AssumeRoleProvider struct {
 	Duration          time.Duration
 	Tags              map[string]string
 	TransitiveTagKeys []string
+	SourceIdentity    string
 	Mfa
 }
 
@@ -84,6 +85,10 @@ func (p *AssumeRoleProvider) assumeRole() (*ststypes.Credentials, error) {
 		input.TransitiveTagKeys = p.TransitiveTagKeys
 	}
 
+	if p.SourceIdentity != "" {
+		input.SourceIdentity = aws.String(p.SourceIdentity)
+	}
+
 	resp, err := p.StsClient.AssumeRole(context.TODO(), input)
 	if err != nil {
 		return nil, err
diff --git a/vault/config.go b/vault/config.go
index b7385a769..fbae0e3f7 100644
--- a/vault/config.go
+++ b/vault/config.go
@@ -147,6 +147,7 @@ type ProfileSection struct {
 	STSRegionalEndpoints    string `ini:"sts_regional_endpoints,omitempty"`
 	SessionTags             string `ini:"session_tags,omitempty"`
 	TransitiveSessionTags   string `ini:"transitive_session_tags,omitempty"`
+	SourceIdentity          string `ini:"source_identity,omitempty"`
 }
 
 func (s ProfileSection) IsEmpty() bool {
@@ -328,6 +329,9 @@ func (cl *ConfigLoader) populateFromConfigFile(config *Config, profileName strin
 	if config.STSRegionalEndpoints == "" {
 		config.STSRegionalEndpoints = psection.STSRegionalEndpoints
 	}
+	if config.SourceIdentity == "" {
+		config.SourceIdentity = psection.SourceIdentity
+	}
 	if sessionTags := psection.SessionTags; sessionTags != "" && config.SessionTags == nil {
 		err := config.SetSessionTags(sessionTags)
 		if err != nil {
@@ -417,7 +421,7 @@ func (cl *ConfigLoader) populateFromEnv(profile *Config) {
 		}
 	}
 
-	// AWS_ROLE_ARN, AWS_ROLE_SESSION_NAME, AWS_SESSION_TAGS and AWS_TRANSITIVE_TAGS only apply to the target profile
+	// AWS_ROLE_ARN, AWS_ROLE_SESSION_NAME, AWS_SESSION_TAGS, AWS_TRANSITIVE_TAGS and AWS_SOURCE_IDENTITY only apply to the target profile
 	if profile.ProfileName == cl.ActiveProfile {
 		if roleARN := os.Getenv("AWS_ROLE_ARN"); roleARN != "" && profile.RoleARN == "" {
 			log.Printf("Using role_arn %q from AWS_ROLE_ARN", roleARN)
@@ -441,6 +445,11 @@ func (cl *ConfigLoader) populateFromEnv(profile *Config) {
 			profile.SetTransitiveSessionTags(transitiveSessionTags)
 			log.Printf("Using transitive_session_tags %v from AWS_TRANSITIVE_TAGS", profile.TransitiveSessionTags)
 		}
+
+		if sourceIdentity := os.Getenv("AWS_SOURCE_IDENTITY"); sourceIdentity != "" && profile.SourceIdentity == "" {
+			profile.SourceIdentity = sourceIdentity
+			log.Printf("Using source_identity %v from AWS_SOURCE_IDENTITY", profile.SourceIdentity)
+		}
 	}
 }
 
@@ -541,6 +550,9 @@ type Config struct {
 
 	// TransitiveSessionTags specifies assumed role Transitive Session Tags keys
 	TransitiveSessionTags []string
+
+	// SourceIdentity specifies assumed role Source Identity
+	SourceIdentity string
 }
 
 // SetSessionTags parses a comma separated key=vaue string and sets Config.SessionTags map
diff --git a/vault/vault.go b/vault/vault.go
index 224ab9146..337b67e59 100644
--- a/vault/vault.go
+++ b/vault/vault.go
@@ -112,6 +112,7 @@ func NewAssumeRoleProvider(credsProvider aws.CredentialsProvider, k keyring.Keyr
 		Duration:          config.AssumeRoleDuration,
 		Tags:              config.SessionTags,
 		TransitiveTagKeys: config.TransitiveSessionTags,
+		SourceIdentity:    config.SourceIdentity,
 		Mfa: Mfa{
 			MfaSerial:       config.MfaSerial,
 			MfaToken:        config.MfaToken,


Test patch: