@@ -69,20 +69,25 @@ def initialize(user = nil, credentials_cache: CredentialsCache.instance)
6969 # Retrieves a valid set of credentials, if possible, or raises
7070 # Auth::InvalidConfiguration.
7171 #
72+ # @param [ Operation::Context | nil ] context Context of the operation
73+ # credentials are retrieved for.
74+ #
7275 # @return [ Auth::Aws::Credentials ] A valid set of credentials.
7376 #
7477 # @raise Auth::InvalidConfiguration if a source contains an invalid set
7578 # of credentials.
7679 # @raise Auth::Aws::CredentialsNotFound if credentials could not be
7780 # retrieved from any source.
78- def credentials
81+ # @raise Error::TimeoutError if credentials cannot be retrieved within
82+ # the timeout defined on the operation context.
83+ def credentials ( context = nil )
7984 credentials = credentials_from_user ( user )
8085 return credentials unless credentials . nil?
8186
8287 credentials = credentials_from_environment
8388 return credentials unless credentials . nil?
8489
85- credentials = @credentials_cache . fetch { obtain_credentials_from_endpoints }
90+ credentials = @credentials_cache . fetch { obtain_credentials_from_endpoints ( context ) }
8691 return credentials unless credentials . nil?
8792
8893 raise Auth ::Aws ::CredentialsNotFound
@@ -127,47 +132,58 @@ def credentials_from_environment
127132
128133 # Returns credentials from the AWS metadata endpoints.
129134 #
135+ # @param [ Operation::Context | nil ] context Context of the operation
136+ # credentials are retrieved for.
137+ #
130138 # @return [ Auth::Aws::Credentials | nil ] A set of credentials, or nil
131139 # if retrieval failed or the obtained credentials are invalid.
132140 #
133141 # @raise Auth::InvalidConfiguration if a source contains an invalid set
134142 # of credentials.
135- def obtain_credentials_from_endpoints
136- if ( credentials = web_identity_credentials ) && credentials_valid? ( credentials , 'Web identity token' )
143+ # @ raise Error::TimeoutError if credentials cannot be retrieved within
144+ # the timeout defined on the operation context.
145+ def obtain_credentials_from_endpoints ( context = nil )
146+ if ( credentials = web_identity_credentials ( context ) ) && credentials_valid? ( credentials , 'Web identity token' )
137147 credentials
138- elsif ( credentials = ecs_metadata_credentials ) && credentials_valid? ( credentials , 'ECS task metadata' )
148+ elsif ( credentials = ecs_metadata_credentials ( context ) ) && credentials_valid? ( credentials , 'ECS task metadata' )
139149 credentials
140- elsif ( credentials = ec2_metadata_credentials ) && credentials_valid? ( credentials , 'EC2 instance metadata' )
150+ elsif ( credentials = ec2_metadata_credentials ( context ) ) && credentials_valid? ( credentials , 'EC2 instance metadata' )
141151 credentials
142152 end
143153 end
144154
145155 # Returns credentials from the EC2 metadata endpoint. The credentials
146156 # could be empty, partial or invalid.
147157 #
158+ # @param [ Operation::Context | nil ] context Context of the operation
159+ # credentials are retrieved for.
160+ #
148161 # @return [ Auth::Aws::Credentials | nil ] A set of credentials, or nil
149162 # if retrieval failed.
150- def ec2_metadata_credentials
163+ # @ raise Error::TimeoutError if credentials cannot be retrieved within
164+ # the timeout defined on the operation context.
165+ def ec2_metadata_credentials ( context = nil )
166+ context &.check_timeout!
151167 http = Net ::HTTP . new ( '169.254.169.254' )
152168 req = Net ::HTTP ::Put . new ( '/latest/api/token' ,
153169 # The TTL is required in order to obtain the metadata token.
154170 { 'x-aws-ec2-metadata-token-ttl-seconds' => '30' } )
155- resp = :: Timeout . timeout ( METADATA_TIMEOUT ) do
171+ resp = with_timeout ( context ) do
156172 http . request ( req )
157173 end
158174 if resp . code != '200'
159175 return nil
160176 end
161177 metadata_token = resp . body
162- resp = :: Timeout . timeout ( METADATA_TIMEOUT ) do
178+ resp = with_timeout ( context ) do
163179 http_get ( http , '/latest/meta-data/iam/security-credentials' , metadata_token )
164180 end
165181 if resp . code != '200'
166182 return nil
167183 end
168184 role_name = resp . body
169185 escaped_role_name = CGI . escape ( role_name ) . gsub ( '+' , '%20' )
170- resp = :: Timeout . timeout ( METADATA_TIMEOUT ) do
186+ resp = with_timeout ( context ) do
171187 http_get ( http , "/latest/meta-data/iam/security-credentials/#{ escaped_role_name } " , metadata_token )
172188 end
173189 if resp . code != '200'
@@ -189,7 +205,18 @@ def ec2_metadata_credentials
189205 return nil
190206 end
191207
192- def ecs_metadata_credentials
208+ # Returns credentials from the ECS metadata endpoint. The credentials
209+ # could be empty, partial or invalid.
210+ #
211+ # @param [ Operation::Context | nil ] context Context of the operation
212+ # credentials are retrieved for.
213+ #
214+ # @return [ Auth::Aws::Credentials | nil ] A set of credentials, or nil
215+ # if retrieval failed.
216+ # @ raise Error::TimeoutError if credentials cannot be retrieved within
217+ # the timeout defined on the operation context.
218+ def ecs_metadata_credentials ( context = nil )
219+ context &.check_timeout!
193220 relative_uri = ENV [ 'AWS_CONTAINER_CREDENTIALS_RELATIVE_URI' ]
194221 if relative_uri . nil? || relative_uri . empty?
195222 return nil
@@ -203,7 +230,7 @@ def ecs_metadata_credentials
203230 # a leading slash must be added by the driver, but this is not
204231 # in fact needed.
205232 req = Net ::HTTP ::Get . new ( relative_uri )
206- resp = :: Timeout . timeout ( METADATA_TIMEOUT ) do
233+ resp = with_timeout ( context ) do
207234 http . request ( req )
208235 end
209236 if resp . code != '200'
@@ -225,13 +252,16 @@ def ecs_metadata_credentials
225252 # inside EKS. See https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html
226253 # for further details.
227254 #
255+ # @param [ Operation::Context | nil ] context Context of the operation
256+ # credentials are retrieved for.
257+ #
228258 # @return [ Auth::Aws::Credentials | nil ] A set of credentials, or nil
229259 # if retrieval failed.
230- def web_identity_credentials
260+ def web_identity_credentials ( context = nil )
231261 web_identity_token , role_arn , role_session_name = prepare_web_identity_inputs
232262 return nil if web_identity_token . nil?
233263 response = request_web_identity_credentials (
234- web_identity_token , role_arn , role_session_name
264+ web_identity_token , role_arn , role_session_name , context
235265 )
236266 return if response . nil?
237267 credentials_from_web_identity_response ( response )
@@ -266,10 +296,16 @@ def prepare_web_identity_inputs
266296 # that the caller is assuming.
267297 # @param [ String ] role_session_name An identifier for the assumed
268298 # role session.
299+ # @param [ Operation::Context | nil ] context Context of the operation
300+ # credentials are retrieved for.
269301 #
270302 # @return [ Net::HTTPResponse | nil ] AWS API response if successful,
271303 # otherwise nil.
272- def request_web_identity_credentials ( token , role_arn , role_session_name )
304+ #
305+ # @ raise Error::TimeoutError if credentials cannot be retrieved within
306+ # the timeout defined on the operation context.
307+ def request_web_identity_credentials ( token , role_arn , role_session_name , context )
308+ context &.check_timeout!
273309 uri = URI ( 'https://sts.amazonaws.com/' )
274310 params = {
275311 'Action' => 'AssumeRoleWithWebIdentity' ,
@@ -281,8 +317,10 @@ def request_web_identity_credentials(token, role_arn, role_session_name)
281317 uri . query = ::URI . encode_www_form ( params )
282318 req = Net ::HTTP ::Post . new ( uri )
283319 req [ 'Accept' ] = 'application/json'
284- resp = Net ::HTTP . start ( uri . hostname , uri . port , use_ssl : true ) do |https |
285- https . request ( req )
320+ resp = with_timeout ( context ) do
321+ Net ::HTTP . start ( uri . hostname , uri . port , use_ssl : true ) do |https |
322+ https . request ( req )
323+ end
286324 end
287325 if resp . code != '200'
288326 return nil
@@ -351,6 +389,28 @@ def credentials_valid?(credentials, source)
351389
352390 true
353391 end
392+
393+ # Execute the given block considering the timeout defined on the context,
394+ # or the default timeout value.
395+ #
396+ # We use +Timeout.timeout+ here because there is no other acceptable easy
397+ # way to time limit http requests.
398+ #
399+ # @param [ Operation::Context | nil ] context Context of the operation
400+ #
401+ # @ raise Error::TimeoutError if deadline exceeded.
402+ def with_timeout ( context )
403+ context &.check_timeout!
404+ timeout = context &.remaining_timeout_sec || METADATA_TIMEOUT
405+ exception_class = if context &.csot?
406+ Error ::TimeoutError
407+ else
408+ nil
409+ end
410+ ::Timeout . timeout ( timeout , exception_class ) do
411+ yield
412+ end
413+ end
354414 end
355415 end
356416 end
0 commit comments