fog-aws 3.33.2 → 3.33.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 342e946932ceb6c12ce470df9e37ffa2f55b9d6a024093b713e6f45feae9f6e0
|
|
4
|
+
data.tar.gz: ff426f59d76f172e598dbea47141718ad28c593bc8ab369b7ff49aba59439163
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3d388030bcc2d14866b610127a8d82b37803d10d98be2156abccd1a49ab983de1b03a54fcd55731808a2f25506b02aed6bb1d0ca327ef9be9763945e0cec8bd5
|
|
7
|
+
data.tar.gz: e26cffa774d294514ef9d89eef55f86dd933d0d4e6afd5858d42b19fa798febb44b030bc209fab4840e03291e9727911961a80db0f6dcedbdfbd7946f287d80f
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [v3.33.3](https://github.com/fog/fog-aws/tree/v3.33.3) (2026-08-28)
|
|
4
|
+
|
|
5
|
+
[Full Changelog](https://github.com/fog/fog-aws/compare/v3.33.0...v3.33.1)
|
|
6
|
+
|
|
7
|
+
**Merged pull requests:**
|
|
8
|
+
|
|
9
|
+
- fix thread safety of shared Fog::Storage connections [\#759](https://github.com/fog/fog-aws/pull/759) ([stanhu](https://github.com/stanhu))
|
|
10
|
+
|
|
11
|
+
- fix flaky describe_spot_price_history [\#760](https://github.com/fog/fog-aws/pull/760) ([stanhu](https://github.com/stanhu))
|
|
12
|
+
|
|
13
|
+
- Bump actions/checkout from 6 to 7 [\#758](https://github.com/fog/fog-aws/pull/758) ([dependabot](https://github.com/apps/dependabot))
|
|
14
|
+
|
|
15
|
+
- Bump actions/dependency-review-action from 4 to 5 [\#757](https://github.com/fog/fog-aws/pull/757) ([dependabot](https://github.com/apps/dependabot))
|
|
16
|
+
|
|
17
|
+
- Bump fog/.github/.github/workflows/ci.yml from 1.5.0 to 1.6.0 [\#756](https://github.com/fog/fog-aws/pull/756) ([dependabot](https://github.com/apps/dependabot))
|
|
18
|
+
|
|
3
19
|
## [v3.33.2](https://github.com/fog/fog-aws/tree/v3.33.2) (2026-04-20)
|
|
4
20
|
|
|
5
21
|
[Full Changelog](https://github.com/fog/fog-aws/compare/v3.33.0...v3.33.1)
|
|
@@ -86,7 +86,9 @@ module Fog
|
|
|
86
86
|
end
|
|
87
87
|
zones = all_zones if zones.nil?
|
|
88
88
|
|
|
89
|
-
|
|
89
|
+
# Fog::Mock.random_numbers(3) can return "000", which .to_i turns
|
|
90
|
+
# into 0 and then trips the validation below, so guarantee at least 1.
|
|
91
|
+
max_results = params['MaxResults'] || [Fog::Mock.random_numbers(3).to_i, 1].max
|
|
90
92
|
if !(max_results.is_a?(Integer) && max_results > 0)
|
|
91
93
|
max_results_error = "InvalidParameterValue => Invalid value '#{max_results}' for maxResults"
|
|
92
94
|
raise Fog::AWS::Compute::Error, max_results_error
|
|
@@ -25,15 +25,18 @@ module Fog
|
|
|
25
25
|
def post_object_hidden_fields(options = {})
|
|
26
26
|
options = options.dup
|
|
27
27
|
if policy = options['policy']
|
|
28
|
+
# Grab the correlated credential state once so the credential,
|
|
29
|
+
# security token, and signature all come from the same snapshot.
|
|
30
|
+
credentials = @credentials
|
|
28
31
|
date = Fog::Time.now
|
|
29
|
-
credential = "#{
|
|
32
|
+
credential = "#{credentials.aws_access_key_id}/#{credentials.signer.credential_scope(date)}"
|
|
30
33
|
extra_conditions = [
|
|
31
34
|
{'x-amz-date' => date.to_iso8601_basic},
|
|
32
35
|
{'x-amz-credential' => credential},
|
|
33
36
|
{'x-amz-algorithm' => Fog::AWS::SignatureV4::ALGORITHM}
|
|
34
37
|
]
|
|
35
38
|
|
|
36
|
-
extra_conditions << {'x-amz-security-token' =>
|
|
39
|
+
extra_conditions << {'x-amz-security-token' => credentials.aws_session_token } if credentials.aws_session_token
|
|
37
40
|
|
|
38
41
|
policy_with_auth_fields = policy.merge('conditions' => policy['conditions'] + extra_conditions)
|
|
39
42
|
|
|
@@ -41,10 +44,10 @@ module Fog
|
|
|
41
44
|
options['X-Amz-Credential'] = credential
|
|
42
45
|
options['X-Amz-Date'] = date.to_iso8601_basic
|
|
43
46
|
options['X-Amz-Algorithm'] = Fog::AWS::SignatureV4::ALGORITHM
|
|
44
|
-
if
|
|
45
|
-
options['X-Amz-Security-Token'] =
|
|
47
|
+
if credentials.aws_session_token
|
|
48
|
+
options['X-Amz-Security-Token'] = credentials.aws_session_token
|
|
46
49
|
end
|
|
47
|
-
options['X-Amz-Signature'] =
|
|
50
|
+
options['X-Amz-Signature'] = credentials.signer.derived_hmac(date).sign(options['policy']).unpack('H*').first
|
|
48
51
|
end
|
|
49
52
|
options
|
|
50
53
|
end
|
data/lib/fog/aws/storage.rb
CHANGED
|
@@ -129,6 +129,32 @@ module Fog
|
|
|
129
129
|
request :upload_part
|
|
130
130
|
request :upload_part_copy
|
|
131
131
|
|
|
132
|
+
# Immutable snapshot of the credential-derived state needed to sign a
|
|
133
|
+
# request: the access key id, secret, session token, and the matching
|
|
134
|
+
# signer (v4) or HMAC (v2). These values are correlated. Under
|
|
135
|
+
# +use_iam_profile+ they are all replaced together whenever credentials
|
|
136
|
+
# are refreshed.
|
|
137
|
+
#
|
|
138
|
+
# Bundling them into one frozen object stored in a single instance
|
|
139
|
+
# variable lets a caller read a consistent set with one atomic
|
|
140
|
+
# reference read (see Real#request). Reading the fields as separate
|
|
141
|
+
# instance variables lets a refresh land between two reads and pair, for
|
|
142
|
+
# example, an old session token with a new signer, which S3 rejects with
|
|
143
|
+
# a 403. Keeping this connection safe to share across threads depends on
|
|
144
|
+
# never mutating an instance; build a new one and swap the reference.
|
|
145
|
+
class Credentials
|
|
146
|
+
attr_reader :aws_access_key_id, :aws_secret_access_key, :aws_session_token, :signer, :hmac
|
|
147
|
+
|
|
148
|
+
def initialize(aws_access_key_id: nil, aws_secret_access_key: nil, aws_session_token: nil, signer: nil, hmac: nil)
|
|
149
|
+
@aws_access_key_id = aws_access_key_id
|
|
150
|
+
@aws_secret_access_key = aws_secret_access_key
|
|
151
|
+
@aws_session_token = aws_session_token
|
|
152
|
+
@signer = signer
|
|
153
|
+
@hmac = hmac
|
|
154
|
+
freeze
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
132
158
|
module Utils
|
|
133
159
|
attr_accessor :region
|
|
134
160
|
attr_accessor :disable_content_md5_validation
|
|
@@ -145,9 +171,10 @@ module Fog
|
|
|
145
171
|
attr_reader :max_copy_chunk_size
|
|
146
172
|
|
|
147
173
|
def cdn
|
|
174
|
+
credentials = @credentials
|
|
148
175
|
@cdn ||= Fog::AWS::CDN.new(
|
|
149
|
-
:aws_access_key_id =>
|
|
150
|
-
:aws_secret_access_key =>
|
|
176
|
+
:aws_access_key_id => credentials.aws_access_key_id,
|
|
177
|
+
:aws_secret_access_key => credentials.aws_secret_access_key,
|
|
151
178
|
:use_iam_profile => @use_iam_profile
|
|
152
179
|
)
|
|
153
180
|
end
|
|
@@ -223,6 +250,10 @@ module Fog
|
|
|
223
250
|
end
|
|
224
251
|
|
|
225
252
|
def v4_signed_params_for_url(params, expires)
|
|
253
|
+
# Read the correlated credential state once, as a single reference,
|
|
254
|
+
# so a concurrent refresh cannot pair a stale session token with a
|
|
255
|
+
# freshly rebuilt signer.
|
|
256
|
+
credentials = @credentials
|
|
226
257
|
now = Fog::Time.now
|
|
227
258
|
|
|
228
259
|
expires = expires - now.to_i
|
|
@@ -232,35 +263,36 @@ module Fog
|
|
|
232
263
|
params[:query]['X-Amz-Expires'] = expires
|
|
233
264
|
params[:query]['X-Amz-Date'] = now.to_iso8601_basic
|
|
234
265
|
|
|
235
|
-
if
|
|
236
|
-
params[:query]['X-Amz-Security-Token'] =
|
|
266
|
+
if credentials.aws_session_token
|
|
267
|
+
params[:query]['X-Amz-Security-Token'] = credentials.aws_session_token
|
|
237
268
|
end
|
|
238
269
|
|
|
239
270
|
params = request_params(params)
|
|
240
271
|
params[:headers][:host] = params[:host]
|
|
241
272
|
params[:headers][:host] += ":#{params[:port]}" if params.fetch(:port, nil)
|
|
242
273
|
|
|
243
|
-
signature_query_params =
|
|
274
|
+
signature_query_params = credentials.signer.signature_parameters(params, now, "UNSIGNED-PAYLOAD")
|
|
244
275
|
params[:query] = (params[:query] || {}).merge(signature_query_params)
|
|
245
276
|
params
|
|
246
277
|
end
|
|
247
278
|
|
|
248
279
|
def v2_signed_params_for_url(params, expires)
|
|
249
|
-
|
|
280
|
+
credentials = @credentials
|
|
281
|
+
if credentials.aws_session_token
|
|
250
282
|
params[:headers]||= {}
|
|
251
|
-
params[:headers]['x-amz-security-token'] =
|
|
283
|
+
params[:headers]['x-amz-security-token'] = credentials.aws_session_token
|
|
252
284
|
end
|
|
253
|
-
signature = signature_v2(params, expires)
|
|
285
|
+
signature = signature_v2(params, expires, credentials.hmac)
|
|
254
286
|
|
|
255
287
|
params = request_params(params)
|
|
256
288
|
|
|
257
289
|
signature_query_params = {
|
|
258
|
-
'AWSAccessKeyId' =>
|
|
290
|
+
'AWSAccessKeyId' => credentials.aws_access_key_id,
|
|
259
291
|
'Signature' => signature,
|
|
260
292
|
'Expires' => expires,
|
|
261
293
|
}
|
|
262
294
|
params[:query] = (params[:query] || {}).merge(signature_query_params)
|
|
263
|
-
params[:query]['x-amz-security-token'] =
|
|
295
|
+
params[:query]['x-amz-security-token'] = credentials.aws_session_token if credentials.aws_session_token
|
|
264
296
|
params
|
|
265
297
|
end
|
|
266
298
|
|
|
@@ -511,25 +543,32 @@ module Fog
|
|
|
511
543
|
end
|
|
512
544
|
|
|
513
545
|
def data
|
|
514
|
-
self.class.data[@region][@aws_access_key_id]
|
|
546
|
+
self.class.data[@region][@credentials.aws_access_key_id]
|
|
515
547
|
end
|
|
516
548
|
|
|
517
549
|
def reset_data
|
|
518
|
-
self.class.data[@region].delete(@aws_access_key_id)
|
|
550
|
+
self.class.data[@region].delete(@credentials.aws_access_key_id)
|
|
519
551
|
end
|
|
520
552
|
|
|
521
553
|
def setup_credentials(options)
|
|
522
554
|
@aws_credentials_refresh_threshold_seconds = options[:aws_credentials_refresh_threshold_seconds]
|
|
523
|
-
|
|
524
|
-
@aws_access_key_id = options[:aws_access_key_id]
|
|
525
|
-
@aws_secret_access_key = options[:aws_secret_access_key]
|
|
526
|
-
@aws_session_token = options[:aws_session_token]
|
|
527
555
|
@aws_credentials_expire_at = options[:aws_credentials_expire_at]
|
|
528
556
|
|
|
529
|
-
|
|
557
|
+
aws_access_key_id = options[:aws_access_key_id]
|
|
558
|
+
aws_secret_access_key = options[:aws_secret_access_key]
|
|
559
|
+
signer = Fog::AWS::SignatureV4.new(aws_access_key_id, aws_secret_access_key, @region, 's3')
|
|
560
|
+
|
|
561
|
+
# Swap the whole credential set as one reference so concurrent
|
|
562
|
+
# readers always observe a complete, self-consistent snapshot.
|
|
563
|
+
@credentials = Credentials.new(
|
|
564
|
+
:aws_access_key_id => aws_access_key_id,
|
|
565
|
+
:aws_secret_access_key => aws_secret_access_key,
|
|
566
|
+
:aws_session_token => options[:aws_session_token],
|
|
567
|
+
:signer => signer
|
|
568
|
+
)
|
|
530
569
|
end
|
|
531
570
|
|
|
532
|
-
def signature_v2(params, expires)
|
|
571
|
+
def signature_v2(params, expires, hmac = nil)
|
|
533
572
|
'foo'
|
|
534
573
|
end
|
|
535
574
|
|
|
@@ -612,16 +651,30 @@ module Fog
|
|
|
612
651
|
@aws_credentials_refresh_threshold_seconds = options[:aws_credentials_refresh_threshold_seconds]
|
|
613
652
|
end
|
|
614
653
|
|
|
615
|
-
@aws_access_key_id = options[:aws_access_key_id]
|
|
616
|
-
@aws_secret_access_key = options[:aws_secret_access_key]
|
|
617
|
-
@aws_session_token = options[:aws_session_token]
|
|
618
654
|
@aws_credentials_expire_at = options[:aws_credentials_expire_at]
|
|
619
655
|
|
|
656
|
+
aws_access_key_id = options[:aws_access_key_id]
|
|
657
|
+
aws_secret_access_key = options[:aws_secret_access_key]
|
|
658
|
+
|
|
659
|
+
signer = hmac = nil
|
|
620
660
|
if @signature_version == 4
|
|
621
|
-
|
|
661
|
+
signer = Fog::AWS::SignatureV4.new(aws_access_key_id, aws_secret_access_key, @region, 's3')
|
|
622
662
|
elsif @signature_version == 2
|
|
623
|
-
|
|
624
|
-
end
|
|
663
|
+
hmac = Fog::HMAC.new('sha1', aws_secret_access_key)
|
|
664
|
+
end
|
|
665
|
+
|
|
666
|
+
# Publish the new credentials as a single frozen reference. This is
|
|
667
|
+
# the only write to the correlated credential state, so a reader
|
|
668
|
+
# that grabs @credentials once (see #request) can never observe a
|
|
669
|
+
# torn mix of old and new values, even while a refresh is running
|
|
670
|
+
# on another thread.
|
|
671
|
+
@credentials = Credentials.new(
|
|
672
|
+
:aws_access_key_id => aws_access_key_id,
|
|
673
|
+
:aws_secret_access_key => aws_secret_access_key,
|
|
674
|
+
:aws_session_token => options[:aws_session_token],
|
|
675
|
+
:signer => signer,
|
|
676
|
+
:hmac => hmac
|
|
677
|
+
)
|
|
625
678
|
end
|
|
626
679
|
|
|
627
680
|
def connection(scheme, host, port)
|
|
@@ -641,18 +694,33 @@ module Fog
|
|
|
641
694
|
def request(params, &block)
|
|
642
695
|
refresh_credentials_if_expired
|
|
643
696
|
|
|
697
|
+
# Read the correlated credential state exactly once, into a local, so
|
|
698
|
+
# a concurrent refresh cannot swap the signer out from under the
|
|
699
|
+
# session token partway through this request. See the Credentials
|
|
700
|
+
# class for the full rationale.
|
|
701
|
+
perform_request(params, @credentials, &block)
|
|
702
|
+
end
|
|
703
|
+
|
|
704
|
+
# Signs and dispatches a single request using the given credential
|
|
705
|
+
# snapshot. Splitting this out of #request lets the redirect retry in
|
|
706
|
+
# #_request reuse one consistent snapshot (with a signer rebuilt for
|
|
707
|
+
# the new region) instead of re-reading, and keeps #request's public
|
|
708
|
+
# signature unchanged.
|
|
709
|
+
def perform_request(params, credentials, &block)
|
|
710
|
+
signer = credentials.signer
|
|
711
|
+
|
|
644
712
|
date = Fog::Time.now
|
|
645
713
|
|
|
646
714
|
params = params.dup
|
|
647
715
|
stringify_query_keys(params)
|
|
648
716
|
params[:headers] = (params[:headers] || {}).dup
|
|
649
717
|
|
|
650
|
-
params[:headers]['x-amz-security-token'] =
|
|
718
|
+
params[:headers]['x-amz-security-token'] = credentials.aws_session_token if credentials.aws_session_token
|
|
651
719
|
|
|
652
720
|
if @signature_version == 2
|
|
653
721
|
expires = date.to_date_header
|
|
654
722
|
params[:headers]['Date'] = expires
|
|
655
|
-
params[:headers]['Authorization'] = "AWS #{
|
|
723
|
+
params[:headers]['Authorization'] = "AWS #{credentials.aws_access_key_id}:#{signature_v2(params, expires, credentials.hmac)}"
|
|
656
724
|
end
|
|
657
725
|
|
|
658
726
|
params = request_params(params)
|
|
@@ -680,12 +748,12 @@ module Fog
|
|
|
680
748
|
else
|
|
681
749
|
params[:headers]['x-amz-content-sha256'] ||= OpenSSL::Digest::SHA256.hexdigest(params[:body] || '')
|
|
682
750
|
end
|
|
683
|
-
signature_components =
|
|
684
|
-
params[:headers]['Authorization'] =
|
|
751
|
+
signature_components = signer.signature_components(params, date, params[:headers]['x-amz-content-sha256'])
|
|
752
|
+
params[:headers]['Authorization'] = signer.components_to_header(signature_components)
|
|
685
753
|
|
|
686
754
|
if params[:body].respond_to?(:read) && @enable_signature_v4_streaming
|
|
687
755
|
body = params.delete :body
|
|
688
|
-
params[:request_block] = S3Streamer.new(body, signature_components['X-Amz-Signature'],
|
|
756
|
+
params[:request_block] = S3Streamer.new(body, signature_components['X-Amz-Signature'], signer, date)
|
|
689
757
|
end
|
|
690
758
|
end
|
|
691
759
|
# FIXME: ToHashParser should make this not needed
|
|
@@ -713,23 +781,32 @@ module Fog
|
|
|
713
781
|
new_params[:bucket_name] = %r{<Bucket>([^<]*)</Bucket>}.match(body).captures.first
|
|
714
782
|
new_params[:host] = %r{<Endpoint>([^<]*)</Endpoint>}.match(body).captures.first
|
|
715
783
|
# some errors provide it directly
|
|
716
|
-
|
|
784
|
+
new_region = %r{<Region>([^<]*)</Region>}.match(body) ? Regexp.last_match.captures.first : nil
|
|
717
785
|
end
|
|
718
786
|
Fog::Logger.warning("fog: followed redirect to #{host}, connecting to the matching region will be more performant")
|
|
719
|
-
|
|
720
|
-
@region = @new_region || case new_params[:host]
|
|
787
|
+
new_region ||= case new_params[:host]
|
|
721
788
|
when /s3.amazonaws.com/, /s3-external-1.amazonaws.com/
|
|
722
789
|
DEFAULT_REGION
|
|
723
790
|
else
|
|
724
791
|
%r{s3[\.\-]([^\.]*).amazonaws.com}.match(new_params[:host]).captures.first
|
|
725
792
|
end
|
|
793
|
+
# Take a fresh, self-consistent snapshot and build a signer for the
|
|
794
|
+
# redirect region locally, then re-dispatch with it. We deliberately
|
|
795
|
+
# do not mutate @region/@signer: doing so would corrupt any
|
|
796
|
+
# concurrent request sharing this connection.
|
|
797
|
+
credentials = @credentials
|
|
726
798
|
if @signature_version == 4
|
|
727
|
-
|
|
799
|
+
redirect_signer = Fog::AWS::SignatureV4.new(credentials.aws_access_key_id, credentials.aws_secret_access_key, new_region, 's3')
|
|
800
|
+
credentials = Credentials.new(
|
|
801
|
+
:aws_access_key_id => credentials.aws_access_key_id,
|
|
802
|
+
:aws_secret_access_key => credentials.aws_secret_access_key,
|
|
803
|
+
:aws_session_token => credentials.aws_session_token,
|
|
804
|
+
:signer => redirect_signer,
|
|
805
|
+
:hmac => credentials.hmac
|
|
806
|
+
)
|
|
728
807
|
original_params[:headers].delete('Authorization')
|
|
729
808
|
end
|
|
730
|
-
|
|
731
|
-
@region, @signer = original_region, original_signer
|
|
732
|
-
response
|
|
809
|
+
perform_request(original_params.merge(new_params), credentials, &block)
|
|
733
810
|
end
|
|
734
811
|
|
|
735
812
|
# See http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-streaming.html
|
|
@@ -793,7 +870,7 @@ DATA
|
|
|
793
870
|
end
|
|
794
871
|
end
|
|
795
872
|
|
|
796
|
-
def signature_v2(params, expires)
|
|
873
|
+
def signature_v2(params, expires, hmac = @credentials.hmac)
|
|
797
874
|
headers = params[:headers] || {}
|
|
798
875
|
|
|
799
876
|
string_to_sign =
|
|
@@ -844,7 +921,7 @@ DATA
|
|
|
844
921
|
end
|
|
845
922
|
canonical_resource << query_string
|
|
846
923
|
string_to_sign << canonical_resource
|
|
847
|
-
signed_string =
|
|
924
|
+
signed_string = hmac.sign(string_to_sign)
|
|
848
925
|
Base64.encode64(signed_string).chomp!
|
|
849
926
|
end
|
|
850
927
|
|
data/lib/fog/aws/version.rb
CHANGED