Amazon-Credentials
view release on metacpan or search on metacpan
lib/Amazon/Credentials.pm view on Meta::CPAN
package Amazon::Credentials;
use strict;
use warnings;
use parent qw/Class::Accessor Exporter/;
__PACKAGE__->follow_best_practice;
__PACKAGE__->mk_accessors(qw/aws_secret_access_key aws_access_key_id token region
user_agent profile debug expiration role container order
serialized logger
/);
use Data::Dumper;
use Date::Format;
use Exporter;
use HTTP::Request;
use JSON;
use LWP::UserAgent;
use POSIX::strptime qw/strptime/;
use Time::Local;
use Scalar::Util qw/reftype/;
use constant AWS_IAM_SECURITY_CREDENTIALS_URL => 'http://169.254.169.254/latest/meta-data/iam/security-credentials/';
use constant AWS_AVAILABILITY_ZONE_URL => 'http://169.254.169.254/latest/meta-data/placement/availability-zone';
use constant AWS_CONTAINER_CREDENTIALS_URL => 'http://169.254.170.2';
use vars qw/$VERSION @EXPORT/;
$VERSION = '1.0.10-1'; $VERSION=~s/\-.*$//;
@EXPORT = qw/$VERSION/;
# we only log at debug level, create a default logger
{
no strict 'refs';
*{'Amazon::Credentials::Logger::debug'} = sub {
shift;
my @tm = localtime(time);
print STDERR sprintf("%s [%s] %s", strftime("%c", @tm), $$, @_);
};
}
=pod
=head1 NAME
C<AWS::Credentials>
=head1 SYNOPSIS
my $aws_creds = new Amazon::Credentials({order => [qw/env file container role/]});
=head1 DESCRIPTION
Class to find AWS credentials from either the environment,
configuration files, instance meta-data or container role.
You can specify the order using the C<order> option in the constructor
to determine the order in which the class will look for credentials.
The default order is I<environent>, I<file>, I<container>, I<instance
meta-data>. See L</new>.
=head1 METHODS
=head2 new
new( options );
my $aws_creds = new Amazon::Credential( { profile => 'sandbox', debug => 1 });
C<options> is a hash of keys that represent options. Any of the
options can also be retrieved using their corresponding 'get_{option}
method.
Options are listed below.
lib/Amazon/Credentials.pm view on Meta::CPAN
=item region
Default region. The class will attempt to find the region in either
the configuration files or the instance unless you specify the region
in the constructor.
=back
=cut
sub new {
my $class = shift;
my $self = $class->SUPER::new(ref($_[0]) ? $_[0] : { @_ });
unless ( $self->get_logger ) {
$self->set_logger(bless {}, 'Amazon::Credentials::Logger');
}
unless ($self->get_user_agent) {
$self->set_user_agent(new LWP::UserAgent);
}
$self->set_profile($ENV{AWS_PROFILE})
unless $self->get_profile;
$self->set_region($ENV{AWS_REGION} || $self->get_default_region)
unless $self->get_region;
unless ( $self->get_aws_secret_access_key && $self->get_aws_access_key_id ) {
$self->set_credentials;
}
$self;
}
=pod
=head2 get_default_region
Returns the region of the currently running instance. The constructor
will set the region to this value unless you set your own C<region>
value. Use C<get_region> to retrieve the value after instantiation or
you can call this method again and it will make a second call to
retrieve the instance metadata.
You can also invoke this as a class method:
$ AWS_REGION=$(perl -MAmazon::Credentials -e 'print Amazon::Credentials::get_default_region;')
=cut
sub get_default_region {
my $self = shift;
# try to get credentials from instance role, but we may not be
# executing on an EC2 or container.
my $url = AWS_AVAILABILITY_ZONE_URL;
my $ua = ref($self) ? $self->get_user_agent : new LWP::UserAgent;
my $req = HTTP::Request->new( GET => $url );
my $region = eval {
my $rsp = $ua->request($req);
# if not 200, then get out of Dodge
die "could not get availability zone\n"
unless $rsp->is_success;
my $region = $rsp->content;
$region =~s/([0-9]+)[a-z]+$/$1/;
$region;
};
return $region;
}
sub set_credentials {
my $self = shift;
my $creds = shift || $self->get_ec2_credentials();
if ( $creds->{aws_secret_access_key} && $creds->{aws_access_key_id} ) {
$self->set_aws_secret_access_key($creds->{aws_secret_access_key});
$self->set_aws_access_key_id($creds->{aws_access_key_id});
$self->set_token($creds->{token});
$self->set_expiration($creds->{expiration});
}
else {
die "no credentials available\n";
}
}
=pod
=head2 get_ec2_credentials (deprecated)
=head2 find_credentidals
find_credentials( option => value, ...);
You normally don't want to use this method. It's automatically invoked
by the constructor if you don't pass in any credentials. Accepts a
hash or hash reference consisting of keys (C<order> or C<profile>) in
the same manner as the constructor.
=cut
sub get_ec2_credentials {
goto &find_credentials;
}
sub find_credentials {
my $self = shift;
my %options = ref($_[0]) ? %{$_[0]} : @_;
$options{order} = $self->get_order || [ qw/env role container file/ ];
$options{profile} = $options{profile} || $self->get_profile;
if (defined $options{profile} ) {
$options{order} = ['file'];
lib/Amazon/Credentials.pm view on Meta::CPAN
return $gmtime;
}
=pod
=head2 get_creds_from_role
get_creds_from_role()
Returns a hash, possibly containing access keys and a token.
=over 5
=item aws_access_key_id
The AWS access key.
=item aws_secret_access_key
The AWS secret key.
=item token
Security token used with access keys.
=item expiration
Token expiration date.
=item role
IAM role if available.
=item source
Will be 'IAM' if role and credentials found.
=back
=cut
sub get_creds_from_role {
my $self = shift;
my $creds = {};
# try to get credentials from instance role
my $url = AWS_IAM_SECURITY_CREDENTIALS_URL;
my $ua = $self->get_user_agent;
my $role;
eval {
# could be infinite, but I don't think so. Either we get an
# error ($@), or a non-200 response code
while ( ! $creds->{token} ) {
$url .= $role if $role;
my $req = HTTP::Request->new( GET => $url );
$self->get_logger->debug(Dumper [ "HTTP REQUEST:\n", $req ])
if $self->get_debug;
my $rsp = $ua->request($req);
$self->get_logger->debug(Dumper [ "HTTP RESPONSE:\n", $rsp ])
if $self->get_debug;
# if not 200, then get out of Dodge
last unless $rsp->is_success;
if ( $role ) {
$creds->{serialized} = $rsp->content;
my $this = from_json($creds->{serialized});
@{$creds}{qw/source role aws_access_key_id aws_secret_access_key token expiration/} =
('IAM',$role, @{$this}{qw/AccessKeyId SecretAccessKey Token Expiration/});
}
else {
$role = $rsp->content;
$self->get_logger->debug(Dumper ['role', $role])
if $self->get_debug;
last unless $role;
}
}
};
$creds->{error} = $@ if $@;
$creds;
}
=pod
=head2 refresh_token
refresh_token() (deprecated)
refresh_credentials()
Retrieves a fresh set of IAM credentials.
if ( $creds->is_token_expired ) {
$creds->refresh_token()
}
=cut
sub refresh_credentials {
goto &refresh_token;
}
sub refresh_token {
my $self = shift;
my $creds;
if ( $self->get_role && $self->get_role eq 'ECS' ) {
$creds = $self->get_creds_from_container;
}
elsif ( $self->get_role ) {
$creds = $self->get_creds_from_role;
}
$self->get_logger->debug(Dumper [$creds])
if $self->get_debug;
die "unable to refresh token!"
unless ref($creds);
$self->set_credentials($creds);
}
sub get_creds_from_container {
my $self = shift;
my $creds = {};
if ( exists $ENV{AWS_CONTAINER_CREDENTIALS_RELATIVE_URI} ) {
# try to get credentials from instance role
my $url = sprintf("%s%s", AWS_CONTAINER_CREDENTIALS_URL, $ENV{AWS_CONTAINER_CREDENTIALS_RELATIVE_URI});
my $ua = $self->get_user_agent;
my $req = HTTP::Request->new( GET => $url );
$req->header("Accept", "*/*");
$self->get_logger->debug(Dumper [ "HTTP REQUEST:\n", $req ])
if $self->get_debug;
$self->get_logger->debug(Dumper [ $req->as_string ])
if $self->get_debug;
my $rsp = $ua->request($req);
$self->get_logger->debug(Dumper [ "HTTP RESPONSE:\n", $rsp ])
if $self->get_debug;
# if not 200, then get out of Dodge
if ( $rsp->is_success ) {
$creds->{serialized} = $rsp->content;
my $this = from_json($rsp->content);
@{$creds}{qw/source container aws_access_key_id aws_secret_access_key token expiration/} =
('IAM','ECS', @{$this}{qw/AccessKeyId SecretAccessKey Token Expiration/});
}
else {
$self->get_logger->debug( "return code: " . $rsp->status_line . "\n");
}
$creds->{error} = $@ if $@;
}
$creds;
}
=pod
=head1 AUTHOR
Rob Lauer - <rlauer6@comcast.net>
=cut
1;
( run in 2.073 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )