Skip to content

Commit

Permalink
Improve credential documentation and expired token error raising (#2694)
Browse files Browse the repository at this point in the history
  • Loading branch information
mullermp authored Apr 25, 2022
1 parent c57a693 commit dedfbdc
Show file tree
Hide file tree
Showing 9 changed files with 63 additions and 39 deletions.
15 changes: 5 additions & 10 deletions gems/aws-sdk-core/lib/aws-sdk-core/assume_role_credentials.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,20 @@
require 'set'

module Aws

# An auto-refreshing credential provider that works by assuming
# a role via {Aws::STS::Client#assume_role}.
# An auto-refreshing credential provider that assumes a role via
# {Aws::STS::Client#assume_role}.
#
# role_credentials = Aws::AssumeRoleCredentials.new(
# client: Aws::STS::Client.new(...),
# role_arn: "linked::account::arn",
# role_session_name: "session-name"
# )
#
# ec2 = Aws::EC2::Client.new(credentials: role_credentials)
#
# If you omit `:client` option, a new {STS::Client} object will be
# constructed.
# If you omit `:client` option, a new {Aws::STS::Client} object will be
# constructed with additional options that were provided.
#
# The AssumeRoleCredentials also provides a `before_refresh` callback
# that can be used to help manage refreshing tokens.
# `before_refresh` is called when AWS credentials are required and need
# to be refreshed and it is called with the AssumeRoleCredentials object.
# @see Aws::STS::Client#assume_role
class AssumeRoleCredentials

include CredentialProvider
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@
require 'base64'

module Aws

# An auto-refreshing credential provider that works by assuming
# a role via {Aws::STS::Client#assume_role_with_web_identity}.
# An auto-refreshing credential provider that assumes a role via
# {Aws::STS::Client#assume_role_with_web_identity}.
#
# role_credentials = Aws::AssumeRoleWebIdentityCredentials.new(
# client: Aws::STS::Client.new(...),
Expand All @@ -16,12 +15,12 @@ module Aws
# role_session_name: "session-name"
# ...
# )
# For full list of parameters accepted
# @see Aws::STS::Client#assume_role_with_web_identity
# ec2 = Aws::EC2::Client.new(credentials: role_credentials)
#
# If you omit `:client` option, a new {Aws::STS::Client} object will be
# constructed with additional options that were provided.
#
# If you omit `:client` option, a new {STS::Client} object will be
# constructed.
# @see Aws::STS::Client#assume_role_with_web_identity
class AssumeRoleWebIdentityCredentials

include CredentialProvider
Expand Down
5 changes: 5 additions & 0 deletions gems/aws-sdk-core/lib/aws-sdk-core/ecs_credentials.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
require 'net/http'

module Aws
# An auto-refreshing credential provider that loads credentials from
# instances running in ECS.
#
# ecs_credentials = Aws::ECSCredentials.new(retries: 3)
# ec2 = Aws::EC2::Client.new(credentials: ecs_credentials)
class ECSCredentials

include CredentialProvider
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
require 'net/http'

module Aws
# An auto-refreshing credential provider that loads credentials from
# EC2 instances.
#
# instance_credentials = Aws::InstanceProfileCredentials.new
# ec2 = Aws::EC2::Client.new(credentials: instance_credentials)
class InstanceProfileCredentials
include CredentialProvider
include RefreshingCredentials
Expand Down
15 changes: 6 additions & 9 deletions gems/aws-sdk-core/lib/aws-sdk-core/process_credentials.rb
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
# frozen_string_literal: true

module Aws

# A credential provider that executes a given process and attempts
# to read its stdout to recieve a JSON payload containing the credentials
#
# Automatically handles refreshing credentials if an Expiration time is
# provided in the credentials payload
#
# credentials = Aws::ProcessCredentials.new('/usr/bin/credential_proc').credentials
# to read its stdout to recieve a JSON payload containing the credentials.
#
# credentials = Aws::ProcessCredentials.new('/usr/bin/credential_proc')
# ec2 = Aws::EC2::Client.new(credentials: credentials)
#
# More documentation on process based credentials can be found here:
# https://docs.aws.amazon.com/cli/latest/topic/config-vars.html#sourcing-credentials-from-external-processes
# Automatically handles refreshing credentials if an Expiration time is
# provided in the credentials payload.
#
# @see https://docs.aws.amazon.com/cli/latest/topic/config-vars.html#sourcing-credentials-from-external-processes
class ProcessCredentials

include CredentialProvider
Expand Down
20 changes: 8 additions & 12 deletions gems/aws-sdk-core/lib/aws-sdk-core/sso_credentials.rb
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
# frozen_string_literal: true

module Aws
# An auto-refreshing credential provider that works by assuming a
# role via {Aws::SSO::Client#get_role_credentials} using a cached access
# token. This class does NOT implement the SSO login token flow - tokens
# An auto-refreshing credential provider that assumes a role via
# {Aws::SSO::Client#get_role_credentials} using a cached access
# token. This class does NOT implement the SSO login token flow - tokens
# must generated and refreshed separately by running `aws login` from the
# AWS CLI with the correct profile.
#
# For more background on AWS SSO see the official
# {https://docs.aws.amazon.com/singlesignon/latest/userguide/what-is.html what is SSO Userguide}
#
# ## Refreshing Credentials from SSO
#
# The `SSOCredentials` will auto-refresh the AWS credentials from SSO. In
# addition to AWS credentials expiring after a given amount of time, the
# access token generated and cached from `aws login` will also expire.
Expand All @@ -20,19 +15,20 @@ module Aws
# the token value, but this can be done by running `aws login` with the
# correct profile.
#
#
# # You must first run aws sso login --profile your-sso-profile
# sso_credentials = Aws::SSOCredentials.new(
# sso_account_id: '123456789',
# sso_role_name: "role_name",
# sso_region: "us-east-1",
# sso_start_url: 'https://your-start-url.awsapps.com/start'
# )
#
# ec2 = Aws::EC2::Client.new(credentials: sso_credentials)
#
# If you omit `:client` option, a new {SSO::Client} object will be
# constructed.
# If you omit `:client` option, a new {Aws::SSO::Client} object will be
# constructed with additional options that were provided.
#
# @see Aws::SSO::Client#get_role_credentials
# @see https://docs.aws.amazon.com/singlesignon/latest/userguide/what-is.html
class SSOCredentials

include CredentialProvider
Expand Down
2 changes: 2 additions & 0 deletions gems/aws-sdk-s3/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
Unreleased Changes
------------------

* Issue - Fix an issue where `ExpiredToken` errors were retried as if the request was from another region.

1.113.1 (2022-04-25)
------------------

Expand Down
7 changes: 6 additions & 1 deletion gems/aws-sdk-s3/lib/aws-sdk-s3/plugins/s3_signer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,8 @@ def call(context)
def handle_region_errors(response)
if wrong_sigv4_region?(response) &&
!fips_region?(response) &&
!custom_endpoint?(response)
!custom_endpoint?(response) &&
!expired_credentials?(response)
get_region_and_retry(response.context)
else
response
Expand All @@ -162,6 +163,10 @@ def fips_region?(resp)
resp.context.http_request.endpoint.host.include?('fips')
end

def expired_credentials?(resp)
resp.context.http_response.body_contents.match(/<Code>ExpiredToken<\/Code>/)
end

def custom_endpoint?(resp)
resolved_suffix = Aws::Partitions::EndpointProvider.dns_suffix_for(
resp.context.config.region,
Expand Down
20 changes: 20 additions & 0 deletions gems/aws-sdk-s3/spec/client/region_detection_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ module S3
'WlSrUk+8d2/rvcnEv2QXer0=</HostId></Error>'
end

let(:expired_credentials_body) do
'<?xml version="1.0" encoding="UTF-8"?>\n<Error><Code>'\
'ExpiredToken</Code><Message>The provided token has expired'\
'</Message><RequestId>531B68B3613F5C96</RequestId>'\
'<HostId>TMnOREh0Ms0touCRX0XkJinw7xqsF0v/iFyA+nCC4d3PpF+k2oek'\
'WlSrUk+8d2/rvcnEv2QXer0=</HostId></Error>'
end

before(:each) do
allow($stderr).to receive(:write)
S3::BUCKET_REGIONS.clear
Expand Down Expand Up @@ -86,6 +94,18 @@ module S3
end
end

context 'expired credentials' do
it 'does not detect region mismatch' do
client = S3::Client.new(client_opts.merge(region: 'us-west-2'))
stub_request(:put, 'https://s3.us-west-2.amazonaws.com/my.bucket/key')
.to_return(status: [400, 'ExpiredToken'], body: expired_credentials_body)

expect do
client.put_object(bucket: 'my.bucket', key: 'key')
end.to raise_error(Aws::S3::Errors::ExpiredToken)
end
end

context 'using an access point ARN' do
it 'detects the moved permanently and redirects' do
stub_request(:put, 'https://myendpoint-123456789012.s3-accesspoint.us-east-1.amazonaws.com/key')
Expand Down

0 comments on commit dedfbdc

Please sign in to comment.