class Google::Auth::ExternalAccount::AwsCredentials
This module handles the retrieval of credentials from Google
Cloud by utilizing the AWS EC2 metadata service and then exchanging the credentials for a short-lived Google
Cloud access token.
Constants
- IMDSV2_TOKEN_EXPIRATION_IN_SECONDS
-
Constant for imdsv2 session token expiration in seconds
Attributes
Will always be nil, but method still gets used.
Public Class Methods
Source
# File lib/googleauth/external_account/aws_credentials.rb, line 37 def initialize options = {} base_setup options @audience = options[:audience] @credential_source = options[:credential_source] || {} @environment_id = @credential_source[:environment_id] @region_url = @credential_source[:region_url] @credential_verification_url = @credential_source[:url] @regional_cred_verification_url = @credential_source[:regional_cred_verification_url] @imdsv2_session_token_url = @credential_source[:imdsv2_session_token_url] # These will be lazily loaded when needed, or will raise an error if not provided @region = nil @request_signer = nil @imdsv2_session_token = nil @imdsv2_session_token_expiry = nil end
Public Instance Methods
Source
# File lib/googleauth/external_account/aws_credentials.rb, line 78 def retrieve_subject_token! if @request_signer.nil? @region = region @request_signer = AwsRequestSigner.new @region end request = { method: "POST", url: @regional_cred_verification_url.sub("{region}", @region) } request_options = @request_signer.generate_signed_request fetch_security_credentials, request request_headers = request_options[:headers] request_headers["x-goog-cloud-target-resource"] = @audience aws_signed_request = { headers: [], method: request_options[:method], url: request_options[:url] } aws_signed_request[:headers] = request_headers.keys.sort.map do |key| { key: key, value: request_headers[key] } end uri_escape aws_signed_request.to_json end
Retrieves the subject token using the credential_source object. The subject token is a serialized [AWS GetCallerIdentity signed request](
https://cloud.google.com/iam/docs/access-resources-aws#exchange-token).
The logic is summarized as:
Retrieve the AWS region from the AWS_REGION or AWS_DEFAULT_REGION environment variable or from the AWS metadata server availability-zone if not found in the environment variable.
Check AWS credentials in environment variables. If not found, retrieve from the AWS metadata server security-credentials endpoint.
When retrieving AWS credentials from the metadata server security-credentials endpoint, the AWS role needs to be determined by # calling the security-credentials endpoint without any argument. Then the credentials can be retrieved via: security-credentials/role_name
Generate the signed request to AWS STS GetCallerIdentity action.
Inject x-goog-cloud-target-resource into header and serialize the signed request. This will be the subject-token to pass to GCP STS.
@return [string] The retrieved subject token.
Private Instance Methods
Source
# File lib/googleauth/external_account/aws_credentials.rb, line 184 def fetch_metadata_role_name unless @credential_verification_url raise "Unable to determine the AWS metadata server security credentials endpoint" end get_aws_resource(@credential_verification_url, "IAM Role").body end
Retrieves the AWS role currently attached to the current AWS workload by querying the AWS metadata server. This is needed for the AWS metadata server security credentials endpoint in order to retrieve the AWS security credentials needed to sign requests to AWS APIs.
Source
# File lib/googleauth/external_account/aws_credentials.rb, line 193 def fetch_metadata_security_credentials role_name response = get_aws_resource "#{@credential_verification_url}/#{role_name}", "credentials" MultiJson.load response.body end
Retrieves the AWS security credentials required for signing AWS requests from the AWS metadata server.
Source
# File lib/googleauth/external_account/aws_credentials.rb, line 157 def fetch_security_credentials env_aws_access_key_id = ENV[CredentialsLoader::AWS_ACCESS_KEY_ID_VAR] env_aws_secret_access_key = ENV[CredentialsLoader::AWS_SECRET_ACCESS_KEY_VAR] # This is normally not available for permanent credentials. env_aws_session_token = ENV[CredentialsLoader::AWS_SESSION_TOKEN_VAR] if env_aws_access_key_id && env_aws_secret_access_key return { access_key_id: env_aws_access_key_id, secret_access_key: env_aws_secret_access_key, session_token: env_aws_session_token } end role_name = fetch_metadata_role_name credentials = fetch_metadata_security_credentials role_name { access_key_id: credentials["AccessKeyId"], secret_access_key: credentials["SecretAccessKey"], session_token: credentials["Token"] } end
Retrieves the AWS security credentials required for signing AWS requests from either the AWS security credentials environment variables or from the AWS metadata server.
Source
# File lib/googleauth/external_account/aws_credentials.rb, line 130 def get_aws_resource url, name, data: nil, headers: {} begin headers["x-aws-ec2-metadata-token"] = imdsv2_session_token response = if data headers["Content-Type"] = "application/json" connection.post url, data, headers else connection.get url, nil, headers end raise Faraday::Error unless response.success? response rescue Faraday::Error raise "Failed to retrieve AWS #{name}." end end
Source
# File lib/googleauth/external_account/aws_credentials.rb, line 109 def imdsv2_session_token return @imdsv2_session_token unless imdsv2_session_token_invalid? raise "IMDSV2 token url must be provided" if @imdsv2_session_token_url.nil? begin response = connection.put @imdsv2_session_token_url do |req| req.headers["x-aws-ec2-metadata-token-ttl-seconds"] = IMDSV2_TOKEN_EXPIRATION_IN_SECONDS.to_s end rescue Faraday::Error => e raise "Fetching AWS IMDSV2 token error: #{e}" end raise Faraday::Error unless response.success? @imdsv2_session_token = response.body @imdsv2_session_token_expiry = Time.now + IMDSV2_TOKEN_EXPIRATION_IN_SECONDS @imdsv2_session_token end
Source
# File lib/googleauth/external_account/aws_credentials.rb, line 125 def imdsv2_session_token_invalid? return true if @imdsv2_session_token.nil? @imdsv2_session_token_expiry.nil? || @imdsv2_session_token_expiry < Time.now end
Source
# File lib/googleauth/external_account/aws_credentials.rb, line 198 def region @region = ENV[CredentialsLoader::AWS_REGION_VAR] || ENV[CredentialsLoader::AWS_DEFAULT_REGION_VAR] unless @region raise "region_url or region must be set for external account credentials" unless @region_url @region ||= get_aws_resource(@region_url, "region").body[0..-2] end @region end
Source
# File lib/googleauth/external_account/aws_credentials.rb, line 147 def uri_escape string if string.nil? nil else CGI.escape(string.encode("UTF-8")).gsub("+", "%20").gsub("%7E", "~") end end