github.com/aavshr/aws-sdk-go@v1.41.3/aws/credentials/stscreds/assume_role_provider.go (about) 1 /* 2 Package stscreds are credential Providers to retrieve STS AWS credentials. 3 4 STS provides multiple ways to retrieve credentials which can be used when making 5 future AWS service API operation calls. 6 7 The SDK will ensure that per instance of credentials.Credentials all requests 8 to refresh the credentials will be synchronized. But, the SDK is unable to 9 ensure synchronous usage of the AssumeRoleProvider if the value is shared 10 between multiple Credentials, Sessions or service clients. 11 12 Assume Role 13 14 To assume an IAM role using STS with the SDK you can create a new Credentials 15 with the SDKs's stscreds package. 16 17 // Initial credentials loaded from SDK's default credential chain. Such as 18 // the environment, shared credentials (~/.aws/credentials), or EC2 Instance 19 // Role. These credentials will be used to to make the STS Assume Role API. 20 sess := session.Must(session.NewSession()) 21 22 // Create the credentials from AssumeRoleProvider to assume the role 23 // referenced by the "myRoleARN" ARN. 24 creds := stscreds.NewCredentials(sess, "myRoleArn") 25 26 // Create service client value configured for credentials 27 // from assumed role. 28 svc := s3.New(sess, &aws.Config{Credentials: creds}) 29 30 Assume Role with static MFA Token 31 32 To assume an IAM role with a MFA token you can either specify a MFA token code 33 directly or provide a function to prompt the user each time the credentials 34 need to refresh the role's credentials. Specifying the TokenCode should be used 35 for short lived operations that will not need to be refreshed, and when you do 36 not want to have direct control over the user provides their MFA token. 37 38 With TokenCode the AssumeRoleProvider will be not be able to refresh the role's 39 credentials. 40 41 // Create the credentials from AssumeRoleProvider to assume the role 42 // referenced by the "myRoleARN" ARN using the MFA token code provided. 43 creds := stscreds.NewCredentials(sess, "myRoleArn", func(p *stscreds.AssumeRoleProvider) { 44 p.SerialNumber = aws.String("myTokenSerialNumber") 45 p.TokenCode = aws.String("00000000") 46 }) 47 48 // Create service client value configured for credentials 49 // from assumed role. 50 svc := s3.New(sess, &aws.Config{Credentials: creds}) 51 52 Assume Role with MFA Token Provider 53 54 To assume an IAM role with MFA for longer running tasks where the credentials 55 may need to be refreshed setting the TokenProvider field of AssumeRoleProvider 56 will allow the credential provider to prompt for new MFA token code when the 57 role's credentials need to be refreshed. 58 59 The StdinTokenProvider function is available to prompt on stdin to retrieve 60 the MFA token code from the user. You can also implement custom prompts by 61 satisfing the TokenProvider function signature. 62 63 Using StdinTokenProvider with multiple AssumeRoleProviders, or Credentials will 64 have undesirable results as the StdinTokenProvider will not be synchronized. A 65 single Credentials with an AssumeRoleProvider can be shared safely. 66 67 // Create the credentials from AssumeRoleProvider to assume the role 68 // referenced by the "myRoleARN" ARN. Prompting for MFA token from stdin. 69 creds := stscreds.NewCredentials(sess, "myRoleArn", func(p *stscreds.AssumeRoleProvider) { 70 p.SerialNumber = aws.String("myTokenSerialNumber") 71 p.TokenProvider = stscreds.StdinTokenProvider 72 }) 73 74 // Create service client value configured for credentials 75 // from assumed role. 76 svc := s3.New(sess, &aws.Config{Credentials: creds}) 77 78 */ 79 package stscreds 80 81 import ( 82 "fmt" 83 "os" 84 "time" 85 86 "github.com/aavshr/aws-sdk-go/aws" 87 "github.com/aavshr/aws-sdk-go/aws/awserr" 88 "github.com/aavshr/aws-sdk-go/aws/client" 89 "github.com/aavshr/aws-sdk-go/aws/credentials" 90 "github.com/aavshr/aws-sdk-go/aws/request" 91 "github.com/aavshr/aws-sdk-go/internal/sdkrand" 92 "github.com/aavshr/aws-sdk-go/service/sts" 93 ) 94 95 // StdinTokenProvider will prompt on stderr and read from stdin for a string value. 96 // An error is returned if reading from stdin fails. 97 // 98 // Use this function to read MFA tokens from stdin. The function makes no attempt 99 // to make atomic prompts from stdin across multiple gorouties. 100 // 101 // Using StdinTokenProvider with multiple AssumeRoleProviders, or Credentials will 102 // have undesirable results as the StdinTokenProvider will not be synchronized. A 103 // single Credentials with an AssumeRoleProvider can be shared safely 104 // 105 // Will wait forever until something is provided on the stdin. 106 func StdinTokenProvider() (string, error) { 107 var v string 108 fmt.Fprintf(os.Stderr, "Assume Role MFA token code: ") 109 _, err := fmt.Scanln(&v) 110 111 return v, err 112 } 113 114 // ProviderName provides a name of AssumeRole provider 115 const ProviderName = "AssumeRoleProvider" 116 117 // AssumeRoler represents the minimal subset of the STS client API used by this provider. 118 type AssumeRoler interface { 119 AssumeRole(input *sts.AssumeRoleInput) (*sts.AssumeRoleOutput, error) 120 } 121 122 type assumeRolerWithContext interface { 123 AssumeRoleWithContext(aws.Context, *sts.AssumeRoleInput, ...request.Option) (*sts.AssumeRoleOutput, error) 124 } 125 126 // DefaultDuration is the default amount of time in minutes that the credentials 127 // will be valid for. 128 var DefaultDuration = time.Duration(15) * time.Minute 129 130 // AssumeRoleProvider retrieves temporary credentials from the STS service, and 131 // keeps track of their expiration time. 132 // 133 // This credential provider will be used by the SDKs default credential change 134 // when shared configuration is enabled, and the shared config or shared credentials 135 // file configure assume role. See Session docs for how to do this. 136 // 137 // AssumeRoleProvider does not provide any synchronization and it is not safe 138 // to share this value across multiple Credentials, Sessions, or service clients 139 // without also sharing the same Credentials instance. 140 type AssumeRoleProvider struct { 141 credentials.Expiry 142 143 // STS client to make assume role request with. 144 Client AssumeRoler 145 146 // Role to be assumed. 147 RoleARN string 148 149 // Session name, if you wish to reuse the credentials elsewhere. 150 RoleSessionName string 151 152 // Optional, you can pass tag key-value pairs to your session. These tags are called session tags. 153 Tags []*sts.Tag 154 155 // A list of keys for session tags that you want to set as transitive. 156 // If you set a tag key as transitive, the corresponding key and value passes to subsequent sessions in a role chain. 157 TransitiveTagKeys []*string 158 159 // Expiry duration of the STS credentials. Defaults to 15 minutes if not set. 160 Duration time.Duration 161 162 // Optional ExternalID to pass along, defaults to nil if not set. 163 ExternalID *string 164 165 // The policy plain text must be 2048 bytes or shorter. However, an internal 166 // conversion compresses it into a packed binary format with a separate limit. 167 // The PackedPolicySize response element indicates by percentage how close to 168 // the upper size limit the policy is, with 100% equaling the maximum allowed 169 // size. 170 Policy *string 171 172 // The ARNs of IAM managed policies you want to use as managed session policies. 173 // The policies must exist in the same account as the role. 174 // 175 // This parameter is optional. You can provide up to 10 managed policy ARNs. 176 // However, the plain text that you use for both inline and managed session 177 // policies can't exceed 2,048 characters. 178 // 179 // An AWS conversion compresses the passed session policies and session tags 180 // into a packed binary format that has a separate limit. Your request can fail 181 // for this limit even if your plain text meets the other requirements. The 182 // PackedPolicySize response element indicates by percentage how close the policies 183 // and tags for your request are to the upper size limit. 184 // 185 // Passing policies to this operation returns new temporary credentials. The 186 // resulting session's permissions are the intersection of the role's identity-based 187 // policy and the session policies. You can use the role's temporary credentials 188 // in subsequent AWS API calls to access resources in the account that owns 189 // the role. You cannot use session policies to grant more permissions than 190 // those allowed by the identity-based policy of the role that is being assumed. 191 // For more information, see Session Policies (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html#policies_session) 192 // in the IAM User Guide. 193 PolicyArns []*sts.PolicyDescriptorType 194 195 // The identification number of the MFA device that is associated with the user 196 // who is making the AssumeRole call. Specify this value if the trust policy 197 // of the role being assumed includes a condition that requires MFA authentication. 198 // The value is either the serial number for a hardware device (such as GAHT12345678) 199 // or an Amazon Resource Name (ARN) for a virtual device (such as arn:aws:iam::123456789012:mfa/user). 200 SerialNumber *string 201 202 // The value provided by the MFA device, if the trust policy of the role being 203 // assumed requires MFA (that is, if the policy includes a condition that tests 204 // for MFA). If the role being assumed requires MFA and if the TokenCode value 205 // is missing or expired, the AssumeRole call returns an "access denied" error. 206 // 207 // If SerialNumber is set and neither TokenCode nor TokenProvider are also 208 // set an error will be returned. 209 TokenCode *string 210 211 // Async method of providing MFA token code for assuming an IAM role with MFA. 212 // The value returned by the function will be used as the TokenCode in the Retrieve 213 // call. See StdinTokenProvider for a provider that prompts and reads from stdin. 214 // 215 // This token provider will be called when ever the assumed role's 216 // credentials need to be refreshed when SerialNumber is also set and 217 // TokenCode is not set. 218 // 219 // If both TokenCode and TokenProvider is set, TokenProvider will be used and 220 // TokenCode is ignored. 221 TokenProvider func() (string, error) 222 223 // ExpiryWindow will allow the credentials to trigger refreshing prior to 224 // the credentials actually expiring. This is beneficial so race conditions 225 // with expiring credentials do not cause request to fail unexpectedly 226 // due to ExpiredTokenException exceptions. 227 // 228 // So a ExpiryWindow of 10s would cause calls to IsExpired() to return true 229 // 10 seconds before the credentials are actually expired. 230 // 231 // If ExpiryWindow is 0 or less it will be ignored. 232 ExpiryWindow time.Duration 233 234 // MaxJitterFrac reduces the effective Duration of each credential requested 235 // by a random percentage between 0 and MaxJitterFraction. MaxJitterFrac must 236 // have a value between 0 and 1. Any other value may lead to expected behavior. 237 // With a MaxJitterFrac value of 0, default) will no jitter will be used. 238 // 239 // For example, with a Duration of 30m and a MaxJitterFrac of 0.1, the 240 // AssumeRole call will be made with an arbitrary Duration between 27m and 241 // 30m. 242 // 243 // MaxJitterFrac should not be negative. 244 MaxJitterFrac float64 245 } 246 247 // NewCredentials returns a pointer to a new Credentials value wrapping the 248 // AssumeRoleProvider. The credentials will expire every 15 minutes and the 249 // role will be named after a nanosecond timestamp of this operation. The 250 // Credentials value will attempt to refresh the credentials using the provider 251 // when Credentials.Get is called, if the cached credentials are expiring. 252 // 253 // Takes a Config provider to create the STS client. The ConfigProvider is 254 // satisfied by the session.Session type. 255 // 256 // It is safe to share the returned Credentials with multiple Sessions and 257 // service clients. All access to the credentials and refreshing them 258 // will be synchronized. 259 func NewCredentials(c client.ConfigProvider, roleARN string, options ...func(*AssumeRoleProvider)) *credentials.Credentials { 260 p := &AssumeRoleProvider{ 261 Client: sts.New(c), 262 RoleARN: roleARN, 263 Duration: DefaultDuration, 264 } 265 266 for _, option := range options { 267 option(p) 268 } 269 270 return credentials.NewCredentials(p) 271 } 272 273 // NewCredentialsWithClient returns a pointer to a new Credentials value wrapping the 274 // AssumeRoleProvider. The credentials will expire every 15 minutes and the 275 // role will be named after a nanosecond timestamp of this operation. The 276 // Credentials value will attempt to refresh the credentials using the provider 277 // when Credentials.Get is called, if the cached credentials are expiring. 278 // 279 // Takes an AssumeRoler which can be satisfied by the STS client. 280 // 281 // It is safe to share the returned Credentials with multiple Sessions and 282 // service clients. All access to the credentials and refreshing them 283 // will be synchronized. 284 func NewCredentialsWithClient(svc AssumeRoler, roleARN string, options ...func(*AssumeRoleProvider)) *credentials.Credentials { 285 p := &AssumeRoleProvider{ 286 Client: svc, 287 RoleARN: roleARN, 288 Duration: DefaultDuration, 289 } 290 291 for _, option := range options { 292 option(p) 293 } 294 295 return credentials.NewCredentials(p) 296 } 297 298 // Retrieve generates a new set of temporary credentials using STS. 299 func (p *AssumeRoleProvider) Retrieve() (credentials.Value, error) { 300 return p.RetrieveWithContext(aws.BackgroundContext()) 301 } 302 303 // RetrieveWithContext generates a new set of temporary credentials using STS. 304 func (p *AssumeRoleProvider) RetrieveWithContext(ctx credentials.Context) (credentials.Value, error) { 305 // Apply defaults where parameters are not set. 306 if p.RoleSessionName == "" { 307 // Try to work out a role name that will hopefully end up unique. 308 p.RoleSessionName = fmt.Sprintf("%d", time.Now().UTC().UnixNano()) 309 } 310 if p.Duration == 0 { 311 // Expire as often as AWS permits. 312 p.Duration = DefaultDuration 313 } 314 jitter := time.Duration(sdkrand.SeededRand.Float64() * p.MaxJitterFrac * float64(p.Duration)) 315 input := &sts.AssumeRoleInput{ 316 DurationSeconds: aws.Int64(int64((p.Duration - jitter) / time.Second)), 317 RoleArn: aws.String(p.RoleARN), 318 RoleSessionName: aws.String(p.RoleSessionName), 319 ExternalId: p.ExternalID, 320 Tags: p.Tags, 321 PolicyArns: p.PolicyArns, 322 TransitiveTagKeys: p.TransitiveTagKeys, 323 } 324 if p.Policy != nil { 325 input.Policy = p.Policy 326 } 327 if p.SerialNumber != nil { 328 if p.TokenCode != nil { 329 input.SerialNumber = p.SerialNumber 330 input.TokenCode = p.TokenCode 331 } else if p.TokenProvider != nil { 332 input.SerialNumber = p.SerialNumber 333 code, err := p.TokenProvider() 334 if err != nil { 335 return credentials.Value{ProviderName: ProviderName}, err 336 } 337 input.TokenCode = aws.String(code) 338 } else { 339 return credentials.Value{ProviderName: ProviderName}, 340 awserr.New("AssumeRoleTokenNotAvailable", 341 "assume role with MFA enabled, but neither TokenCode nor TokenProvider are set", nil) 342 } 343 } 344 345 var roleOutput *sts.AssumeRoleOutput 346 var err error 347 348 if c, ok := p.Client.(assumeRolerWithContext); ok { 349 roleOutput, err = c.AssumeRoleWithContext(ctx, input) 350 } else { 351 roleOutput, err = p.Client.AssumeRole(input) 352 } 353 354 if err != nil { 355 return credentials.Value{ProviderName: ProviderName}, err 356 } 357 358 // We will proactively generate new credentials before they expire. 359 p.SetExpiration(*roleOutput.Credentials.Expiration, p.ExpiryWindow) 360 361 return credentials.Value{ 362 AccessKeyID: *roleOutput.Credentials.AccessKeyId, 363 SecretAccessKey: *roleOutput.Credentials.SecretAccessKey, 364 SessionToken: *roleOutput.Credentials.SessionToken, 365 ProviderName: ProviderName, 366 }, nil 367 }