Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve credential documentation and expired token error raising #2694

Merged
merged 5 commits into from
Apr 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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