class AwsMfaSecure::Base

Public Instance Methods

aws_cli_installed?() click to toggle source
# File lib/aws_mfa_secure/base.rb, line 34
def aws_cli_installed?
  return false unless File.exist?("#{ENV['HOME']}/.aws")
  system("type aws > /dev/null 2>&1")
end
aws_cli_setup?() click to toggle source
# File lib/aws_mfa_secure/base.rb, line 39
def aws_cli_setup?
  File.exist?("#{ENV['HOME']}/.aws/config") &&
  File.exist?("#{ENV['HOME']}/.aws/credentials")
end
aws_config(prop) click to toggle source
# File lib/aws_mfa_secure/base.rb, line 134
def aws_config(prop)
  profile_data = AWSConfig[aws_profile]
  return unless profile_data
  v = profile_data[prop.to_s]
  v unless v.blank?
end
aws_mfa_env_set?() click to toggle source
# File lib/aws_mfa_secure/base.rb, line 28
def aws_mfa_env_set?
  ENV['AWS_ACCESS_KEY_ID'] &&
  ENV['AWS_SECRET_ACCESS_KEY'] &&
  ENV['AWS_MFA_SERIAL']
end
aws_profile() click to toggle source
# File lib/aws_mfa_secure/base.rb, line 142
def aws_profile
  ENV['AWS_PROFILE'] || 'default'
end
credentials() click to toggle source
# File lib/aws_mfa_secure/base.rb, line 55
def credentials
  JSON.load(IO.read(session_creds_path))
end
fetch_creds?() click to toggle source
# File lib/aws_mfa_secure/base.rb, line 44
def fetch_creds?
  !good_session_creds?
end
get_session_token(shell: false) click to toggle source
# File lib/aws_mfa_secure/base.rb, line 70
def get_session_token(shell: false)
  retries = 0
  begin
    token_code = mfa_prompt
    options = {
      serial_number: mfa_serial,
      token_code: token_code,
    }
    options[:duration_seconds] = ENV['AWS_MFA_TTL'] if ENV['AWS_MFA_TTL']

    if shell
      shell_get_session_token(options) # mimic ruby sdk
    else # ruby sdk
      sts.get_session_token(options)
    end
  rescue Aws::STS::Errors::ValidationError, Aws::STS::Errors::AccessDenied, MfaError => e
    $stderr.puts "#{e.class}: #{e.message}"
    $stderr.puts "Incorrect MFA code.  Please try again."
    retries += 1
    if retries >= 3
      $stderr.puts "Giving up after #{retries} retries."
      exit 1
    end
    retry
  end
end
good_session_creds?() click to toggle source
# File lib/aws_mfa_secure/base.rb, line 48
def good_session_creds?
  return false unless File.exist?(session_creds_path)

  expiration = Time.parse(credentials["expiration"])
  Time.now.utc < expiration # not expired
end
iam_mfa?() click to toggle source
# File lib/aws_mfa_secure/base.rb, line 13
def iam_mfa?
  return true if aws_mfa_env_set?
  return false unless aws_cli_installed? && aws_cli_setup?
  return false unless mfa_serial

  # The iam_mfa? check will only return true for the case when mfa_serial is set and access keys are used.
  # This is because for assume role cases, the current aws cli tool supports mfa_serial already.
  # Sending session AWS based access keys intefere with the current aws cli assume role mfa_serial support
  aws_access_key_id = aws_config(:aws_access_key_id)
  aws_secret_access_key = aws_config(:aws_secret_access_key)
  source_profile = aws_config(:source_profile)

  aws_access_key_id && aws_secret_access_key && !source_profile
end
mfa_prompt() click to toggle source
# File lib/aws_mfa_secure/base.rb, line 97
def mfa_prompt
  if ENV['AWS_MFA_TOKEN']
    token_code = ENV.delete('AWS_MFA_TOKEN') # only use once, prompt afterwards if incorrect
    return token_code
  end

  $stderr.print "Please provide your MFA code: "
  $stdin.gets.strip
end
mfa_serial() click to toggle source
# File lib/aws_mfa_secure/base.rb, line 125
def mfa_serial
  ENV['AWS_MFA_SERIAL'] || aws_config(:mfa_serial)
end
save_creds(credentials) click to toggle source
# File lib/aws_mfa_secure/base.rb, line 60
def save_creds(credentials)
  FileUtils.mkdir_p(File.dirname(session_creds_path))
  IO.write(session_creds_path, JSON.pretty_generate(credentials))
  flush_cache # Clear memo cache. Not needed for brand new temp credentials, but needed when updating existing ones
end
session_creds_path() click to toggle source
# File lib/aws_mfa_secure/base.rb, line 66
def session_creds_path
  "#{SESSIONS_PATH}/#{@aws_profile}"
end
shell_get_session_token(options) click to toggle source

Credentials class uses this version of get-session-token to allow the AWS Ruby SDK itself to be patched.

# File lib/aws_mfa_secure/base.rb, line 108
def shell_get_session_token(options)
  args = options.map { |k,v| "--#{k.to_s.gsub('_','-')} #{v}" }.join(' ')
  command = "aws sts get-session-token #{args} 2>&1"
  # puts "=> #{command}" # uncomment for debugging
  out = `#{command}`

  unless out.include?("Credentials")
    raise(MfaError, out.strip) # custom error
  end

  data = JSON.load(out)
  resp = data.deep_transform_keys { |k| k.underscore }
  # mimic ruby sdk resp
  credentials = Aws::STS::Types::Credentials.new(resp["credentials"])
  Aws::STS::Types::GetSessionTokenResponse.new(credentials: credentials)
end
sts() click to toggle source
# File lib/aws_mfa_secure/base.rb, line 129
def sts
  Aws::STS::Client.new
end