1. Gem::
  2. S3URISigner

class Gem::S3URISigner

S3URISigner implements AWS SigV4 for S3 Source to avoid a dependency on the aws-sdk-* gems More on AWS SigV4:docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html

Constants

BASE64_URI_TRANSLATE
EC2_IAM_INFO
EC2_IAM_SECURITY_CREDENTIALS
EC2_IAM_TOKEN
S3Config

Attributes

method[RW]
uri[RW]

Public Class Methods

Source
# File lib/rubygems/s3_uri_signer.rb, line 35definitialize(uri,method)@uri =uri@method =methodend

Public Instance Methods

Source
# File lib/rubygems/s3_uri_signer.rb, line 42defsign(expiration =86_400)s3_config =fetch_s3_configcurrent_time =Time.now.utcdate_time =current_time.strftime("%Y%m%dT%H%M%SZ")date =date_time[0,8]credential_info ="#{date}/#{s3_config.region}/s3/aws4_request"canonical_host ="#{uri.host}.s3.#{s3_config.region}.amazonaws.com"query_params =generate_canonical_query_params(s3_config,date_time,credential_info,expiration)canonical_request =generate_canonical_request(canonical_host,query_params)string_to_sign =generate_string_to_sign(date_time,credential_info,canonical_request)signature =generate_signature(s3_config,date,string_to_sign)Gem::URI.parse("https://#{canonical_host}#{uri.path}?#{query_params}&X-Amz-Signature=#{signature}")end

Signs S3URI using query-params according to the reference:docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html

Private Instance Methods

Source
# File lib/rubygems/s3_uri_signer.rb, line 143defbase64_uri_escape(str)str.gsub(%r{[\+/=\n]},BASE64_URI_TRANSLATE)end
Source
# File lib/rubygems/s3_uri_signer.rb, line 216defcreate_request_pool(uri)proxy_uri =Gem::Request.proxy_uri(Gem::Request.get_proxy_from_env(uri.scheme))certs =Gem::Request.get_cert_filesGem::Request::ConnectionPools.new(proxy_uri,certs).pool_for(uri)end
Source
# File lib/rubygems/s3_uri_signer.rb, line 211defec2_iam_request(uri,verb)@request_pool||=create_request_pool(uri)Gem::Request.new(uri,verb,nil,@request_pool)end
Source
# File lib/rubygems/s3_uri_signer.rb, line 172defec2_metadata_credentials_imds_v1iam_info =ec2_metadata_request(EC2_IAM_INFO,token:nil)# Expected format: arn:aws:iam::<id>:instance-profile/<role_name>role_name =iam_info["InstanceProfileArn"].split("/").lastec2_metadata_request(EC2_IAM_SECURITY_CREDENTIALS+role_name,token:nil)end
Source
# File lib/rubygems/s3_uri_signer.rb, line 164defec2_metadata_credentials_imds_v2token =ec2_metadata_tokeniam_info =ec2_metadata_request(EC2_IAM_INFO,token:)# Expected format: arn:aws:iam::<id>:instance-profile/<role_name>role_name =iam_info["InstanceProfileArn"].split("/").lastec2_metadata_request(EC2_IAM_SECURITY_CREDENTIALS+role_name,token:)end
Source
# File lib/rubygems/s3_uri_signer.rb, line 147defec2_metadata_credentials_jsonrequire_relative"vendored_net_http"require_relative"request"require_relative"request/connection_pools"require"json"# First try V2 fallback to V1res =nilbeginres =ec2_metadata_credentials_imds_v2rescueInstanceProfileErroralert_warning"Unable to access ec2 credentials via IMDSv2, falling back to IMDSv1"res =ec2_metadata_credentials_imds_v1endresend
Source
# File lib/rubygems/s3_uri_signer.rb, line 179defec2_metadata_request(url,token:)request =ec2_iam_request(Gem::URI(url),Gem::Net::HTTP::Get)response =request.fetchdo|req|iftokenreq.add_field"X-aws-ec2-metadata-token",tokenendendcaseresponsewhenGem::Net::HTTPOKthenJSON.parse(response.body)elseraiseInstanceProfileError.new("Unable to fetch AWS metadata from #{uri}: #{response.message} #{response.code}")endend
Source
# File lib/rubygems/s3_uri_signer.rb, line 196defec2_metadata_tokenrequest =ec2_iam_request(Gem::URI(EC2_IAM_TOKEN),Gem::Net::HTTP::Put)response =request.fetchdo|req|req.add_field"X-aws-ec2-metadata-token-ttl-seconds",60endcaseresponsewhenGem::Net::HTTPOKthenresponse.bodyelseraiseInstanceProfileError.new("Unable to fetch AWS metadata from #{uri}: #{response.message} #{response.code}")endend
Source
# File lib/rubygems/s3_uri_signer.rb, line 110deffetch_s3_configreturnS3Config.new(uri.user,uri.password,nil,"us-east-1")ifuri.user&&uri.passwords3_source =Gem.configuration[:s3_source]||Gem.configuration["s3_source"]host =uri.hostraiseConfigurationError.new("no s3_source key exists in .gemrc")unlesss3_sourceauth =s3_source[host]||s3_source[host.to_sym]raiseConfigurationError.new("no key for host #{host} in s3_source in .gemrc")unlessauthprovider =auth[:provider]||auth["provider"]caseproviderwhen"env"id =ENV["AWS_ACCESS_KEY_ID"]secret =ENV["AWS_SECRET_ACCESS_KEY"]security_token =ENV["AWS_SESSION_TOKEN"]when"instance_profile"credentials =ec2_metadata_credentials_jsonid =credentials["AccessKeyId"]secret =credentials["SecretAccessKey"]security_token =credentials["Token"]elseid =auth[:id]||auth["id"]secret =auth[:secret]||auth["secret"]security_token =auth[:security_token]||auth["security_token"]endraiseConfigurationError.new("s3_source for #{host} missing id or secret")unlessid&&secretregion =auth[:region]||auth["region"]||"us-east-1"S3Config.new(id,secret,security_token,region)end

Extracts S3 configuration for S3 bucket

Source
# File lib/rubygems/s3_uri_signer.rb, line 64defgenerate_canonical_query_params(s3_config,date_time,credential_info,expiration)canonical_params = {}canonical_params["X-Amz-Algorithm"] ="AWS4-HMAC-SHA256"canonical_params["X-Amz-Credential"] ="#{s3_config.access_key_id}/#{credential_info}"canonical_params["X-Amz-Date"] =date_timecanonical_params["X-Amz-Expires"] =expiration.to_scanonical_params["X-Amz-SignedHeaders"] ="host"canonical_params["X-Amz-Security-Token"] =s3_config.security_tokenifs3_config.security_token# Sorting is required to generate proper signaturecanonical_params.sort.to_h.mapdo|key,value|"#{base64_uri_escape(key)}=#{base64_uri_escape(value)}"end.join("&")end
Source
# File lib/rubygems/s3_uri_signer.rb, line 79defgenerate_canonical_request(canonical_host,query_params)  [method.upcase,uri.path,query_params,"host:#{canonical_host}","",# empty params"host","UNSIGNED-PAYLOAD",  ].join("\n")end
Source
# File lib/rubygems/s3_uri_signer.rb, line 100defgenerate_signature(s3_config,date,string_to_sign)date_key =OpenSSL::HMAC.digest("sha256","AWS4"+s3_config.secret_access_key,date)date_region_key =OpenSSL::HMAC.digest("sha256",date_key,s3_config.region)date_region_service_key =OpenSSL::HMAC.digest("sha256",date_region_key,"s3")signing_key =OpenSSL::HMAC.digest("sha256",date_region_service_key,"aws4_request")OpenSSL::HMAC.hexdigest("sha256",signing_key,string_to_sign)end
Source
# File lib/rubygems/s3_uri_signer.rb, line 91defgenerate_string_to_sign(date_time,credential_info,canonical_request)  ["AWS4-HMAC-SHA256",date_time,credential_info,OpenSSL::Digest::SHA256.hexdigest(canonical_request),  ].join("\n")end