Module: Awskeyring::Awsapi

Defined in:
lib/awskeyring/awsapi.rb

Overview

AWS API methods for Awskeyring

Constant Summary collapse

ADMIN_POLICY =

Admin policy as json

{
  Version: '2012-10-17',
  Statement: [{
    Action: '*',
    Resource: '*',
    Effect: 'Allow'
  }]
}.to_json.freeze
AWS_SIGNIN_URL =

AWS Signin url

'https://signin.aws.amazon.com/federation'
AWS_ENV_VARS =

AWS Env vars

%w[
  AWS_ACCOUNT_NAME
  AWS_ACCOUNT_ID
  AWS_ACCESS_KEY_ID
  AWS_ACCESS_KEY
  AWS_CREDENTIAL_EXPIRATION
  AWS_SECRET_ACCESS_KEY
  AWS_SECRET_KEY
  AWS_SECURITY_TOKEN
  AWS_SESSION_TOKEN
].freeze
TWELVE_HOUR =

Twelve hours in seconds

(60 * 60 * 12)
ONE_HOUR =

One hour in seconds

(60 * 60 * 1)
ONE_DAY =

Days in seconds

(24 * 60 * 60)

Class Method Summary collapse

Class Method Details

.get_account_id(key:) ⇒ String

Get the account number from an access key

Parameters:

  • key (String)

    The aws_access_key_id

Returns:

  • (String)

    Account number



239
240
241
242
243
244
# File 'lib/awskeyring/awsapi.rb', line 239

def self.(key:)
  padded_no = key[3..12]
  mask = (2 << 39) - 1
  decimal = (decode(padded_no) >> 4) & mask
  decimal.to_s.rjust(12, '0')
end

.get_cred_json(key:, secret:, token:, expiry:) ⇒ String

Genarates AWS CLI compatible JSON see credential_process in AWS Docs

Parameters:

  • key (String)

    The aws_access_key_id

  • secret (String)

    The aws_secret_access_key

  • token (String)

    The aws_session_token

  • expiry (String)

    expiry time

Returns:

  • (String)

    credential_process json



109
110
111
112
113
114
115
116
117
# File 'lib/awskeyring/awsapi.rb', line 109

def self.get_cred_json(key:, secret:, token:, expiry:)
  JSON.pretty_generate(
    Version: 1,
    AccessKeyId: key,
    SecretAccessKey: secret,
    SessionToken: token,
    Expiration: expiry
  )
end

.get_credentials_from_file(account:) ⇒ Hash

Retrieve credentials from the AWS Credentials file

Parameters:

  • account (String)

    the profile name wanted

Returns:

  • (Hash)

    with the new credentials key The aws_access_key_id secret The aws_secret_access_key token The aws_session_token expiry expiry time



171
172
173
174
175
176
177
178
179
180
181
# File 'lib/awskeyring/awsapi.rb', line 171

def self.get_credentials_from_file(account:)
  creds = Aws::SharedCredentials.new(profile_name: )
  {
    account: ,
    key: creds.credentials.access_key_id,
    secret: creds.credentials.secret_access_key,
    token: creds.credentials.session_token,
    expiry: Time.new + TWELVE_HOUR,
    role: nil
  }
end

.get_env_array(params = {}) ⇒ Hash

Generates Environment Variables for the AWS CLI

Parameters:

  • params (Hash) (defaults to: {})

    including

    String

    account The aws account name

    String

    key The aws_access_key_id

    String

    secret The aws_secret_access_key

    String

    token The aws_session_token

Returns:

  • (Hash)

    env_var hash



127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/awskeyring/awsapi.rb', line 127

def self.get_env_array(params = {}) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/AbcSize
  env_var = {}
  env_var['AWS_DEFAULT_REGION'] = 'us-east-1' unless region

  params[:expiration] = Time.at(params[:expiry]).iso8601 unless params[:expiry].nil?
  params[:account_name] = params.delete(:account)
  params[:account_id] = (key: params[:key]) unless params[:key].nil?

  AWS_ENV_VARS.each do |var_name|
    params.each_key do |param_name|
      if var_name.include?(param_name.to_s.upcase) && !params[param_name].nil?
        env_var[var_name] = params[param_name]
      end
    end
  end

  env_var
end

.get_login_url(key:, secret:, token:, path:, user:) ⇒ String

Retrieves an AWS Console login url

Parameters:

  • key (String)

    The aws_access_key_id

  • secret (String)

    The aws_secret_access_key

  • token (String)

    The aws_session_token

  • user (String)

    The local username

  • path (String)

    within the Console to access

Returns:

  • (String)

    login_url to access



191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/awskeyring/awsapi.rb', line 191

def self.(key:, secret:, token:, path:, user:)
  console_url = "https://console.aws.amazon.com/#{path}/home"

  unless token
    cred = get_token({ key: key, secret: secret, user: user, duration: TWELVE_HOUR })
    key = cred[:key]
    secret = cred[:secret]
    token = cred[:token]
  end

  session_json = {
    sessionId: key,
    sessionKey: secret,
    sessionToken: token
  }.to_json

  destination_param = "&Destination=#{CGI.escape(console_url)}"

  "#{AWS_SIGNIN_URL}?Action=login#{token_param(session_json: session_json)}#{destination_param}"
end

.get_token(params = {}) ⇒ Hash

Retrieves a temporary session token from AWS

Parameters:

  • params (Hash) (defaults to: {})

    including key The aws_access_key_id secret The aws_secret_access_key user The local username mfa The users MFA arn code The MFA code duration time in seconds until expiry role_arn ARN of the role to assume

Returns:

  • (Hash)

    with the new credentials key The aws_access_key_id secret The aws_secret_access_key token The aws_session_token expiry expiry time



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/awskeyring/awsapi.rb', line 60

def self.get_token(params = {}) # rubocop:disable  Metrics/AbcSize, Metrics/MethodLength
  ENV['AWS_DEFAULT_REGION'] = 'us-east-1' unless region
  sts = Aws::STS::Client.new(access_key_id: params[:key], secret_access_key: params[:secret])

  params[:mfa] = nil unless params[:code]
  begin
    response =
      if params[:role_arn]
        sts.assume_role(
          duration_seconds: params[:duration].to_i,
          role_arn: params[:role_arn],
          role_session_name: params[:user],
          serial_number: params[:mfa],
          token_code: params[:code]
        )
      elsif params[:code]
        sts.get_session_token(
          duration_seconds: params[:duration].to_i,
          serial_number: params[:mfa],
          token_code: params[:code]
        )
      else
        sts.get_federation_token(
          name: params[:user],
          policy: ADMIN_POLICY,
          duration_seconds: params[:duration]
        )
      end
  rescue Aws::STS::Errors::AccessDenied => e
    warn e
    exit 1
  end

  {
    key: response.credentials[:access_key_id],
    secret: response.credentials[:secret_access_key],
    token: response.credentials[:session_token],
    expiry: response.credentials[:expiration]
  }
end

.regionString

Get the current region

Returns:

  • (String)

    current configured region



229
230
231
232
233
# File 'lib/awskeyring/awsapi.rb', line 229

def self.region
  keys = %w[AWS_REGION AMAZON_REGION AWS_DEFAULT_REGION]
  region = ENV.values_at(*keys).compact.first
  region || Aws.shared_config.region(profile: 'default')
end

.retry_backoff(&block) ⇒ Object

Retry the call with backoff

Parameters:

  • block (Block)

    the block to retry.



296
297
298
299
300
301
302
303
304
305
306
307
308
309
# File 'lib/awskeyring/awsapi.rb', line 296

def self.retry_backoff(&block)
  retries ||= 1
  begin
    yield block
  rescue Aws::IAM::Errors::InvalidClientTokenId => e
    if retries < 4
      sleep 2**retries
      retries += 1
      retry
    end
    warn e.message
    exit 1
  end
end

.rotate(account:, key:, secret:, key_message:) ⇒ String

Rotates the AWS access keys

Parameters:

  • key (String)

    The aws_access_key_id

  • secret (String)

    The aws_secret_access_key

  • account (String)

    the associated account name.

Returns:

  • (String)

    key The aws_access_key_id

  • (String)

    secret The aws_secret_access_key

  • (String)

    account the associated account name.



267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
# File 'lib/awskeyring/awsapi.rb', line 267

def self.rotate(account:, key:, secret:, key_message:) # rubocop:disable  Metrics/MethodLength
  ENV['AWS_DEFAULT_REGION'] = 'us-east-1' unless region
  iam = Aws::IAM::Client.new(access_key_id: key, secret_access_key: secret)

  if iam.list_access_keys[:access_key_metadata].length > 1
    warn key_message
    exit 1
  end

  new_key = iam.create_access_key[:access_key]
  iam = Aws::IAM::Client.new(
    access_key_id: new_key[:access_key_id],
    secret_access_key: new_key[:secret_access_key]
  )
  retry_backoff do
    iam.delete_access_key(
      access_key_id: key
    )
  end
  {
    account: ,
    key: new_key[:access_key_id],
    secret: new_key[:secret_access_key]
  }
end

.verify_cred(key:, secret:, token: nil) ⇒ Object

Verify Credentials are active and valid

Parameters:

  • key (String)

    The aws_access_key_id

  • secret (String)

    The aws_secret_access_key

  • token (String) (defaults to: nil)

    The aws_session_token



151
152
153
154
155
156
157
158
159
160
161
# File 'lib/awskeyring/awsapi.rb', line 151

def self.verify_cred(key:, secret:, token: nil)
  begin
    ENV['AWS_DEFAULT_REGION'] = 'us-east-1' unless region
    sts = Aws::STS::Client.new(access_key_id: key, secret_access_key: secret, session_token: token)
    sts.get_caller_identity
  rescue Aws::Errors::ServiceError => e
    warn e
    exit 1
  end
  true
end