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

[v2] support opt-in regional sts endpoint #2106

Merged
merged 4 commits into from Oct 23, 2019
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
@@ -1,6 +1,8 @@
Unreleased Changes
------------------

* Feature - Aws::Core - Support enable STS regional endpoints by `sts_regional_endpoints: 'regional'`

2.11.379 (2019-10-22)
------------------

Expand Down
1 change: 1 addition & 0 deletions aws-sdk-core/lib/aws-sdk-core.rb
Expand Up @@ -327,6 +327,7 @@ module Plugins
autoload :S3RequestSigner, 'aws-sdk-core/plugins/s3_request_signer'
autoload :S3SseCpk, 'aws-sdk-core/plugins/s3_sse_cpk'
autoload :S3UrlEncodedKeys, 'aws-sdk-core/plugins/s3_url_encoded_keys'
autoload :STSRegionalEndpoints, 'aws-sdk-core/plugins/sts_regional_endpoints'
autoload :SQSQueueUrls, 'aws-sdk-core/plugins/sqs_queue_urls'
autoload :SQSMd5s, 'aws-sdk-core/plugins/sqs_md5s'
autoload :StubResponses, 'aws-sdk-core/plugins/stub_responses'
Expand Down
4 changes: 4 additions & 0 deletions aws-sdk-core/lib/aws-sdk-core/api/customizations.rb
Expand Up @@ -274,6 +274,10 @@ def is_eventstream?(api, shape_name)
Aws::Plugins::S3RequestSigner
))

plugins('AWS Security Token Service', add: %w(
Aws::Plugins::STSRegionalEndpoints
))

plugins('AWS S3 Control', add: %w(
Aws::Plugins::S3HostId
Aws::Plugins::S3ControlDns
Expand Down
39 changes: 33 additions & 6 deletions aws-sdk-core/lib/aws-sdk-core/endpoint_provider.rb
Expand Up @@ -2,12 +2,34 @@ module Aws
# @api private
class EndpointProvider

# when sts_regional_endpoint set to `legacy`
# endpoint pattern stays global for
# following regions
STS_LEGACY_REGIONS = %w(
ap-northeast-1
ap-south-1
ap-southeast-1
ap-southeast-2
aws-global
ca-central-1
eu-central-1
eu-north-1
eu-west-1
eu-west-2
eu-west-3
sa-east-1
us-east-1
us-east-2
us-west-1
us-west-2
)

def initialize(rules)
@rules = rules
end

def resolve(region, service)
"https://" + endpoint_for(region, service)
def resolve(region, service, sts_regional_endpoints = nil)
"https://" + endpoint_for(region, service, sts_regional_endpoints)
end

def signing_region(region, service)
Expand All @@ -27,16 +49,21 @@ def dns_suffix_for(region)

private

def endpoint_for(region, service)
def endpoint_for(region, service, sts_regional_endpoints)
partition = get_partition(region)
endpoint = default_endpoint(partition, service, region)
service_cfg = partition.fetch("services", {}).fetch(service, {})

# Check for service-level default endpoint.
endpoint = service_cfg.fetch("defaults", {}).fetch("hostname", endpoint)

# Check for sts legacy behavior
sts_legacy = service == 'sts' &&
sts_regional_endpoints == 'legacy' &&
STS_LEGACY_REGIONS.include?(region)

# Check for global endpoint.
if service_cfg["isRegionalized"] == false
if sts_legacy || service_cfg["isRegionalized"] == false
region = service_cfg.fetch("partitionEndpoint", region)
end

Expand Down Expand Up @@ -81,8 +108,8 @@ def default_partition

class << self

def resolve(region, service)
default_provider.resolve(region, service)
def resolve(region, service, sts_regional_endpoints = nil)
default_provider.resolve(region, service, sts_regional_endpoints)
end

def signing_region(region, service)
Expand Down
2 changes: 2 additions & 0 deletions aws-sdk-core/lib/aws-sdk-core/plugins/regional_endpoint.rb
Expand Up @@ -26,6 +26,8 @@ class RegionalEndpoint < Seahorse::Client::Plugin
endpoint_prefix = cfg.api.metadata['endpointPrefix']
if cfg.region && endpoint_prefix
EndpointProvider.resolve(cfg.region, endpoint_prefix)
sts_regional = cfg.respond_to?(:sts_regional_endpoints) ? cfg.sts_regional_endpoints : nil
EndpointProvider.resolve(cfg.region, endpoint_prefix, sts_regional)
end
end

Expand Down
30 changes: 30 additions & 0 deletions aws-sdk-core/lib/aws-sdk-core/plugins/sts_regional_endpoints.rb
@@ -0,0 +1,30 @@
module Aws
module Plugins

class STSRegionalEndpoints < Seahorse::Client::Plugin

option(:sts_regional_endpoints,
default: 'legacy',
doc_type: String,
docstring: <<-DOCS) do |cfg|
Passing in 'regional' to enable regional endpoint for STS for all supported
regions (except 'aws-global'), defaults to 'legacy' mode, using global endpoint
for legacy regions.
DOCS
resolve_sts_regional_endpoints(cfg)
end

private

def self.resolve_sts_regional_endpoints(cfg)
env_mode = ENV['AWS_STS_REGIONAL_ENDPOINTS']
env_mode = nil if env_mode == ''
cfg_mode = Aws.shared_config.sts_regional_endpoints(
profile: cfg.profile)
env_mode || cfg_mode || 'legacy'
end

end

end
end
15 changes: 15 additions & 0 deletions aws-sdk-core/lib/aws-sdk-core/shared_config.rb
Expand Up @@ -128,6 +128,21 @@ def region(opts = {})
end
end

def sts_regional_endpoints(opts = {})
p = opts[:profile] || @profile_name
if @config_enabled
if @parsed_credentials
mode = @parsed_credentials.fetch(p, {})["sts_regional_endpoints"]
end
if @parsed_config
mode ||= @parsed_config.fetch(p, {})["sts_regional_endpoints"]
end
mode
else
nil
end
end

def endpoint_discovery(opts = {})
p = opts[:profile] || @profile_name
if @config_enabled && @parsed_config
Expand Down
20 changes: 20 additions & 0 deletions aws-sdk-core/spec/aws/shared_config_spec.rb
Expand Up @@ -110,6 +110,26 @@ module Aws
expect(config.endpoint_discovery).to eq("false")
end

context 'sts_regional_endpoints selection' do

it 'can resolve sts_regional_endpoints from config file' do
config = SharedConfig.new(
config_path: mock_config_file,
config_enabled: true,
profile_name: "sts_regional"
)
expect(config.sts_regional_endpoints).to eq('regional')

config = SharedConfig.new(
config_path: mock_config_file,
config_enabled: true,
profile_name: "sts_legacy"
)
expect(config.sts_regional_endpoints).to eq('legacy')
end

end

end
end
end
165 changes: 165 additions & 0 deletions aws-sdk-core/spec/aws/sts/sts_regional_endpoints_spec.rb
@@ -0,0 +1,165 @@
require_relative '../../spec_helper'

module Aws
module STS
describe Client do

describe ':sts_regional_endpoints' do

let(:mock_config_file) {
File.expand_path(File.join(File.dirname(__FILE__),
'..', 'fixtures', 'credentials', 'mock_shared_config'))
}

it 'uses ENV before shared config' do
ENV['AWS_STS_REGIONAL_ENDPOINTS'] = 'regional'
config = SharedConfig.new(
config_path: mock_config_file,
config_enabled: true,
profile_name: "sts_legacy"
)

allow(Aws).to receive(:shared_config).and_return(config)
client = Client.new(
stub_responses: true,
region: 'us-west-2'
)
expect(client.config.sts_regional_endpoints).to eq('regional')
end

it 'defaults to `legacy`' do
client = Client.new(
stub_responses: true,
region: 'us-west-2'
)
expect(client.config.sts_regional_endpoints).to eq('legacy')
resp = client.get_caller_identity
expect(resp.context.http_request.endpoint.to_s).to eq(
'https://sts.amazonaws.com')
end

it 'can be set `regional` in the constructor' do
client = Client.new(
stub_responses: true,
sts_regional_endpoints: 'regional',
region: 'us-west-2'
)
expect(client.config.sts_regional_endpoints).to eq('regional')
resp = client.get_caller_identity
expect(resp.context.http_request.endpoint.to_s).to eq(
'https://sts.us-west-2.amazonaws.com')
end

it 'can be set fron ENV' do
ENV['AWS_STS_REGIONAL_ENDPOINTS'] = 'regional'
client = Client.new(
stub_responses: true,
region: 'us-west-2'
)
expect(client.config.sts_regional_endpoints).to eq('regional')
resp = client.get_caller_identity
expect(resp.context.http_request.endpoint.to_s).to eq(
'https://sts.us-west-2.amazonaws.com')
end

it 'has no effect on fips endpoint' do
client = Client.new(
stub_responses: true,
region: 'us-west-2-fips'
)
expect(client.config.sts_regional_endpoints).to eq('legacy')
resp = client.get_caller_identity
expect(resp.context.http_request.endpoint.to_s).to eq(
'https://sts-fips.us-west-2.amazonaws.com')

client = Client.new(
stub_responses: true,
sts_regional_endpoints: 'regional',
region: 'us-west-2-fips'
)
resp = client.get_caller_identity
expect(resp.context.http_request.endpoint.to_s).to eq(
'https://sts-fips.us-west-2.amazonaws.com')
end

it 'has no effect on aws-global even when `regional`' do
client = Client.new(
stub_responses: true,
sts_regional_endpoints: 'regional',
region: 'aws-global'
)
expect(client.config.sts_regional_endpoints).to eq('regional')
resp = client.get_caller_identity
expect(resp.context.http_request.endpoint.to_s).to eq(
'https://sts.amazonaws.com')
end

it 'has no effect on current regionalized regions' do
client = Client.new(
stub_responses: true,
region: 'ap-east-1'
)
expect(client.config.sts_regional_endpoints).to eq('legacy')
resp = client.get_caller_identity
expect(resp.context.http_request.endpoint.to_s).to eq(
'https://sts.ap-east-1.amazonaws.com')

client = Client.new(
stub_responses: true,
sts_regional_endpoints: 'regional',
region: 'ap-east-1'
)
expect(client.config.sts_regional_endpoints).to eq('regional')
resp = client.get_caller_identity
expect(resp.context.http_request.endpoint.to_s).to eq(
'https://sts.ap-east-1.amazonaws.com')
end

it 'has no effect on cn-north-1 region' do
client = Client.new(
stub_responses: true,
region: 'cn-north-1'
)
expect(client.config.sts_regional_endpoints).to eq('legacy')
resp = client.get_caller_identity
expect(resp.context.http_request.endpoint.to_s).to eq(
'https://sts.cn-north-1.amazonaws.com.cn')

client = Client.new(
stub_responses: true,
sts_regional_endpoints: 'regional',
region: 'cn-north-1'
)
expect(client.config.sts_regional_endpoints).to eq('regional')
resp = client.get_caller_identity
expect(resp.context.http_request.endpoint.to_s).to eq(
'https://sts.cn-north-1.amazonaws.com.cn')
end

it 'configures properly at config' do
client = Client.new(
stub_responses: true,
region: 'us-west-2'
)
expect(client.config.sts_regional_endpoints).to eq('legacy')
expect(client.config.region).to eq('us-west-2')
expect(client.config.sigv4_region).to eq('us-east-1')
expect(client.config.endpoint.to_s).to eq('https://sts.amazonaws.com')

client = Client.new(
stub_responses: true,
sts_regional_endpoints: 'regional',
region: 'us-west-2'
)
expect(client.config.sts_regional_endpoints).to eq('regional')
expect(client.config.region).to eq('us-west-2')
expect(client.config.sigv4_region).to eq('us-west-2')
expect(client.config.endpoint.to_s).to eq(
'https://sts.us-west-2.amazonaws.com')
end

end

end
end
end
10 changes: 10 additions & 0 deletions aws-sdk-core/spec/fixtures/credentials/mock_shared_config
Expand Up @@ -78,3 +78,13 @@ endpoint_discovery_enabled = true
aws_access_key_id = AKID
aws_secret_access_key = SECRET
endpoint_discovery_enabled = false

[profile sts_regional]
aws_access_key_id = AKID
aws_secret_access_key = SECRET
sts_regional_endpoints = regional

[profile sts_legacy]
aws_access_key_id = AKID
aws_secret_access_key = SECRET
sts_regional_endpoints = legacy