assume_role_provider.go (13457B)
1 // Package stscreds are credential Providers to retrieve STS AWS credentials. 2 // 3 // STS provides multiple ways to retrieve credentials which can be used when making 4 // future AWS service API operation calls. 5 // 6 // The SDK will ensure that per instance of credentials.Credentials all requests 7 // to refresh the credentials will be synchronized. But, the SDK is unable to 8 // ensure synchronous usage of the AssumeRoleProvider if the value is shared 9 // between multiple Credentials or service clients. 10 // 11 // # Assume Role 12 // 13 // To assume an IAM role using STS with the SDK you can create a new Credentials 14 // with the SDKs's stscreds package. 15 // 16 // // Initial credentials loaded from SDK's default credential chain. Such as 17 // // the environment, shared credentials (~/.aws/credentials), or EC2 Instance 18 // // Role. These credentials will be used to to make the STS Assume Role API. 19 // cfg, err := config.LoadDefaultConfig(context.TODO()) 20 // if err != nil { 21 // panic(err) 22 // } 23 // 24 // // Create the credentials from AssumeRoleProvider to assume the role 25 // // referenced by the "myRoleARN" ARN. 26 // stsSvc := sts.NewFromConfig(cfg) 27 // creds := stscreds.NewAssumeRoleProvider(stsSvc, "myRoleArn") 28 // 29 // cfg.Credentials = aws.NewCredentialsCache(creds) 30 // 31 // // Create service client value configured for credentials 32 // // from assumed role. 33 // svc := s3.NewFromConfig(cfg) 34 // 35 // # Assume Role with custom MFA Token provider 36 // 37 // To assume an IAM role with a MFA token you can either specify a custom MFA 38 // token provider or use the SDK's built in StdinTokenProvider that will prompt 39 // the user for a token code each time the credentials need to to be refreshed. 40 // Specifying a custom token provider allows you to control where the token 41 // code is retrieved from, and how it is refreshed. 42 // 43 // With a custom token provider, the provider is responsible for refreshing the 44 // token code when called. 45 // 46 // cfg, err := config.LoadDefaultConfig(context.TODO()) 47 // if err != nil { 48 // panic(err) 49 // } 50 // 51 // staticTokenProvider := func() (string, error) { 52 // return someTokenCode, nil 53 // } 54 // 55 // // Create the credentials from AssumeRoleProvider to assume the role 56 // // referenced by the "myRoleARN" ARN using the MFA token code provided. 57 // creds := stscreds.NewAssumeRoleProvider(sts.NewFromConfig(cfg), "myRoleArn", func(o *stscreds.AssumeRoleOptions) { 58 // o.SerialNumber = aws.String("myTokenSerialNumber") 59 // o.TokenProvider = staticTokenProvider 60 // }) 61 // 62 // cfg.Credentials = aws.NewCredentialsCache(creds) 63 // 64 // // Create service client value configured for credentials 65 // // from assumed role. 66 // svc := s3.NewFromConfig(cfg) 67 // 68 // # Assume Role with MFA Token Provider 69 // 70 // To assume an IAM role with MFA for longer running tasks where the credentials 71 // may need to be refreshed setting the TokenProvider field of AssumeRoleProvider 72 // will allow the credential provider to prompt for new MFA token code when the 73 // role's credentials need to be refreshed. 74 // 75 // The StdinTokenProvider function is available to prompt on stdin to retrieve 76 // the MFA token code from the user. You can also implement custom prompts by 77 // satisfying the TokenProvider function signature. 78 // 79 // Using StdinTokenProvider with multiple AssumeRoleProviders, or Credentials will 80 // have undesirable results as the StdinTokenProvider will not be synchronized. A 81 // single Credentials with an AssumeRoleProvider can be shared safely. 82 // 83 // cfg, err := config.LoadDefaultConfig(context.TODO()) 84 // if err != nil { 85 // panic(err) 86 // } 87 // 88 // // Create the credentials from AssumeRoleProvider to assume the role 89 // // referenced by the "myRoleARN" ARN using the MFA token code provided. 90 // creds := stscreds.NewAssumeRoleProvider(sts.NewFromConfig(cfg), "myRoleArn", func(o *stscreds.AssumeRoleOptions) { 91 // o.SerialNumber = aws.String("myTokenSerialNumber") 92 // o.TokenProvider = stscreds.StdinTokenProvider 93 // }) 94 // 95 // cfg.Credentials = aws.NewCredentialsCache(creds) 96 // 97 // // Create service client value configured for credentials 98 // // from assumed role. 99 // svc := s3.NewFromConfig(cfg) 100 package stscreds 101 102 import ( 103 "context" 104 "fmt" 105 "time" 106 107 "github.com/aws/aws-sdk-go-v2/aws" 108 "github.com/aws/aws-sdk-go-v2/service/sts" 109 "github.com/aws/aws-sdk-go-v2/service/sts/types" 110 ) 111 112 // StdinTokenProvider will prompt on stdout and read from stdin for a string value. 113 // An error is returned if reading from stdin fails. 114 // 115 // Use this function go read MFA tokens from stdin. The function makes no attempt 116 // to make atomic prompts from stdin across multiple gorouties. 117 // 118 // Using StdinTokenProvider with multiple AssumeRoleProviders, or Credentials will 119 // have undesirable results as the StdinTokenProvider will not be synchronized. A 120 // single Credentials with an AssumeRoleProvider can be shared safely 121 // 122 // Will wait forever until something is provided on the stdin. 123 func StdinTokenProvider() (string, error) { 124 var v string 125 fmt.Printf("Assume Role MFA token code: ") 126 _, err := fmt.Scanln(&v) 127 128 return v, err 129 } 130 131 // ProviderName provides a name of AssumeRole provider 132 const ProviderName = "AssumeRoleProvider" 133 134 // AssumeRoleAPIClient is a client capable of the STS AssumeRole operation. 135 type AssumeRoleAPIClient interface { 136 AssumeRole(ctx context.Context, params *sts.AssumeRoleInput, optFns ...func(*sts.Options)) (*sts.AssumeRoleOutput, error) 137 } 138 139 // DefaultDuration is the default amount of time in minutes that the 140 // credentials will be valid for. This value is only used by AssumeRoleProvider 141 // for specifying the default expiry duration of an assume role. 142 // 143 // Other providers such as WebIdentityRoleProvider do not use this value, and 144 // instead rely on STS API's default parameter handing to assign a default 145 // value. 146 var DefaultDuration = time.Duration(15) * time.Minute 147 148 // AssumeRoleProvider retrieves temporary credentials from the STS service, and 149 // keeps track of their expiration time. 150 // 151 // This credential provider will be used by the SDKs default credential change 152 // when shared configuration is enabled, and the shared config or shared credentials 153 // file configure assume role. See Session docs for how to do this. 154 // 155 // AssumeRoleProvider does not provide any synchronization and it is not safe 156 // to share this value across multiple Credentials, Sessions, or service clients 157 // without also sharing the same Credentials instance. 158 type AssumeRoleProvider struct { 159 options AssumeRoleOptions 160 } 161 162 // AssumeRoleOptions is the configurable options for AssumeRoleProvider 163 type AssumeRoleOptions struct { 164 // Client implementation of the AssumeRole operation. Required 165 Client AssumeRoleAPIClient 166 167 // IAM Role ARN to be assumed. Required 168 RoleARN string 169 170 // Session name, if you wish to uniquely identify this session. 171 RoleSessionName string 172 173 // Expiry duration of the STS credentials. Defaults to 15 minutes if not set. 174 Duration time.Duration 175 176 // Optional ExternalID to pass along, defaults to nil if not set. 177 ExternalID *string 178 179 // The policy plain text must be 2048 bytes or shorter. However, an internal 180 // conversion compresses it into a packed binary format with a separate limit. 181 // The PackedPolicySize response element indicates by percentage how close to 182 // the upper size limit the policy is, with 100% equaling the maximum allowed 183 // size. 184 Policy *string 185 186 // The ARNs of IAM managed policies you want to use as managed session policies. 187 // The policies must exist in the same account as the role. 188 // 189 // This parameter is optional. You can provide up to 10 managed policy ARNs. 190 // However, the plain text that you use for both inline and managed session 191 // policies can't exceed 2,048 characters. 192 // 193 // An AWS conversion compresses the passed session policies and session tags 194 // into a packed binary format that has a separate limit. Your request can fail 195 // for this limit even if your plain text meets the other requirements. The 196 // PackedPolicySize response element indicates by percentage how close the policies 197 // and tags for your request are to the upper size limit. 198 // 199 // Passing policies to this operation returns new temporary credentials. The 200 // resulting session's permissions are the intersection of the role's identity-based 201 // policy and the session policies. You can use the role's temporary credentials 202 // in subsequent AWS API calls to access resources in the account that owns 203 // the role. You cannot use session policies to grant more permissions than 204 // those allowed by the identity-based policy of the role that is being assumed. 205 // For more information, see Session Policies (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html#policies_session) 206 // in the IAM User Guide. 207 PolicyARNs []types.PolicyDescriptorType 208 209 // The identification number of the MFA device that is associated with the user 210 // who is making the AssumeRole call. Specify this value if the trust policy 211 // of the role being assumed includes a condition that requires MFA authentication. 212 // The value is either the serial number for a hardware device (such as GAHT12345678) 213 // or an Amazon Resource Name (ARN) for a virtual device (such as arn:aws:iam::123456789012:mfa/user). 214 SerialNumber *string 215 216 // The source identity specified by the principal that is calling the AssumeRole 217 // operation. You can require users to specify a source identity when they assume a 218 // role. You do this by using the sts:SourceIdentity condition key in a role trust 219 // policy. You can use source identity information in CloudTrail logs to determine 220 // who took actions with a role. You can use the aws:SourceIdentity condition key 221 // to further control access to Amazon Web Services resources based on the value of 222 // source identity. For more information about using source identity, see Monitor 223 // and control actions taken with assumed roles 224 // (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_control-access_monitor.html) 225 // in the IAM User Guide. 226 SourceIdentity *string 227 228 // Async method of providing MFA token code for assuming an IAM role with MFA. 229 // The value returned by the function will be used as the TokenCode in the Retrieve 230 // call. See StdinTokenProvider for a provider that prompts and reads from stdin. 231 // 232 // This token provider will be called when ever the assumed role's 233 // credentials need to be refreshed when SerialNumber is set. 234 TokenProvider func() (string, error) 235 236 // A list of session tags that you want to pass. Each session tag consists of a key 237 // name and an associated value. For more information about session tags, see 238 // Tagging STS Sessions 239 // (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html) in the 240 // IAM User Guide. This parameter is optional. You can pass up to 50 session tags. 241 Tags []types.Tag 242 243 // A list of keys for session tags that you want to set as transitive. If you set a 244 // tag key as transitive, the corresponding key and value passes to subsequent 245 // sessions in a role chain. For more information, see Chaining Roles with Session 246 // Tags 247 // (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html#id_session-tags_role-chaining) 248 // in the IAM User Guide. This parameter is optional. 249 TransitiveTagKeys []string 250 } 251 252 // NewAssumeRoleProvider constructs and returns a credentials provider that 253 // will retrieve credentials by assuming a IAM role using STS. 254 func NewAssumeRoleProvider(client AssumeRoleAPIClient, roleARN string, optFns ...func(*AssumeRoleOptions)) *AssumeRoleProvider { 255 o := AssumeRoleOptions{ 256 Client: client, 257 RoleARN: roleARN, 258 } 259 260 for _, fn := range optFns { 261 fn(&o) 262 } 263 264 return &AssumeRoleProvider{ 265 options: o, 266 } 267 } 268 269 // Retrieve generates a new set of temporary credentials using STS. 270 func (p *AssumeRoleProvider) Retrieve(ctx context.Context) (aws.Credentials, error) { 271 // Apply defaults where parameters are not set. 272 if len(p.options.RoleSessionName) == 0 { 273 // Try to work out a role name that will hopefully end up unique. 274 p.options.RoleSessionName = fmt.Sprintf("aws-go-sdk-%d", time.Now().UTC().UnixNano()) 275 } 276 if p.options.Duration == 0 { 277 // Expire as often as AWS permits. 278 p.options.Duration = DefaultDuration 279 } 280 input := &sts.AssumeRoleInput{ 281 DurationSeconds: aws.Int32(int32(p.options.Duration / time.Second)), 282 PolicyArns: p.options.PolicyARNs, 283 RoleArn: aws.String(p.options.RoleARN), 284 RoleSessionName: aws.String(p.options.RoleSessionName), 285 ExternalId: p.options.ExternalID, 286 SourceIdentity: p.options.SourceIdentity, 287 Tags: p.options.Tags, 288 TransitiveTagKeys: p.options.TransitiveTagKeys, 289 } 290 if p.options.Policy != nil { 291 input.Policy = p.options.Policy 292 } 293 if p.options.SerialNumber != nil { 294 if p.options.TokenProvider != nil { 295 input.SerialNumber = p.options.SerialNumber 296 code, err := p.options.TokenProvider() 297 if err != nil { 298 return aws.Credentials{}, err 299 } 300 input.TokenCode = aws.String(code) 301 } else { 302 return aws.Credentials{}, fmt.Errorf("assume role with MFA enabled, but TokenProvider is not set") 303 } 304 } 305 306 resp, err := p.options.Client.AssumeRole(ctx, input) 307 if err != nil { 308 return aws.Credentials{Source: ProviderName}, err 309 } 310 311 return aws.Credentials{ 312 AccessKeyID: *resp.Credentials.AccessKeyId, 313 SecretAccessKey: *resp.Credentials.SecretAccessKey, 314 SessionToken: *resp.Credentials.SessionToken, 315 Source: ProviderName, 316 317 CanExpire: true, 318 Expires: *resp.Credentials.Expiration, 319 }, nil 320 }