Skip to content

Commit

Permalink
Merge tag 'release-0.45'
Browse files Browse the repository at this point in the history
  • Loading branch information
pplu committed Jun 1, 2022
2 parents b2132fe + 3dbb907 commit 50c1d22
Show file tree
Hide file tree
Showing 15 changed files with 215 additions and 16 deletions.
1 change: 0 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ jobs:
- "5.16"
- "5.14"
- "5.12"
- "5.10"
# on macos-latest, only test with latest version of perl.
# because concurrency of builds on macos-latest is limit and it takes long time.
include:
Expand Down
8 changes: 7 additions & 1 deletion Changes
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
0.44
0.45 1 Jun 2022
- Drop support for Perl 5.10, since Getopt-Long-Descriptive, a dependency of one of our dependencies,
is supporting Perl >= 5.12.
- Account for ContainerProfile security token expiration (dheffx)
- Added support for Support AWS Instance Metadata Service V2 (IMDSv2) (thanks 0leksii)

0.44 14 Jul 2021
- Added GitHub Actions as CI (thanks shogo82148)
- Automatic naming of new APIs from botocore definition files
- Parallel and consistent generation of SDK (thanks aeruder PR #408 and #409)
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -472,3 +472,7 @@ aeruder for contributing
- Completing speedups and benchmarking code
- Substituting Config::INI for Config::AWS
- Parrallelizing and fixing generation inconsistencies of the SDK

dheffx for making ContainerProfile credential provider more robust

0leksii for building support for Instance Metadata Service v2 (IMDSv2)
8 changes: 7 additions & 1 deletion auto-lib/Paws.pm
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ __PACKAGE__->meta->make_immutable;

package Paws;

our $VERSION = '0.44';
our $VERSION = '0.45';

use Carp;

Expand Down Expand Up @@ -927,6 +927,8 @@ L<Paws::Credential::File> - Gets credentials from AWS SDK config files
L<Paws::Credential::InstanceProfile> - Gets credentials from the InstanceProfile (Role) of the running instance
L<Paws::Credential::InstanceProfileV2> - Gets credentials from the InstanceProfile (Role) of the running instance using IMDSv2 approach
L<Paws::Credential::STS> - Gets temporary credentials from the Secure Token Service
L<Paws::Credential::AssumeRole> - Gets temporary credentials with AssumeRole
Expand Down Expand Up @@ -1249,5 +1251,9 @@ aeruder for contributing
- Substituting Config::INI for Config::AWS
- Parrallelizing and fixing generation inconsistencies of the SDK
dheffx for making ContainerProfile credential provider more robust
0leksii for building support for Instance Metadata Service v2 (IMDSv2)
=cut
2 changes: 1 addition & 1 deletion builder-lib/Paws/API/Builder/Paws.pm
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ package Paws::API::Builder::Paws {
use Moose;

sub version {
'0.44';
'0.45';
}

has boto_service_files => (is => 'ro', isa => 'ArrayRef[Str]', lazy => 1, default => sub {
Expand Down
2 changes: 1 addition & 1 deletion cpanfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
requires 'perl' => '5.010001';
requires 'perl' => '5.012001';
requires 'Moose';
requires 'MooseX::ClassAttribute';
requires 'HTTP::Tiny';
Expand Down
2 changes: 1 addition & 1 deletion lib/Paws/Credential/ECSContainerProfile.pm
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ package Paws::Credential::ECSContainerProfile;
sub _refresh {
my $self = shift;

return if $self->expiration >= time;
return if $self->expiration - 240 >= time;
return if ! $self->metadata_url;

my $ua = $self->ua;
Expand Down
2 changes: 1 addition & 1 deletion lib/Paws/Credential/InstanceProfile.pm
Original file line number Diff line number Diff line change
Expand Up @@ -114,4 +114,4 @@ service is not present (not running on an AWS instance), the connection has to t
A user agent that has a C<get> method. Defaults to HTTP::Tiny
=cut
=cut
135 changes: 135 additions & 0 deletions lib/Paws/Credential/InstanceProfileV2.pm
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package Paws::Credential::InstanceProfileV2;
use JSON::MaybeXS;
use Moose;
use DateTime::Format::ISO8601;
with 'Paws::Credential';

has metadata_url => (
is => 'ro',
isa => 'Str',
default => 'http://169.254.169.254/latest/meta-data/iam/security-credentials/'
);

has token_url => (
is => 'ro',
isa => 'Str',
default => 'http://169.254.169.254/latest/api/token'
);

has timeout => (is => 'ro', isa => 'Int', default => 1);

has ua => (
is => 'ro',
lazy => 1,
default => sub {
my $self = shift;
use HTTP::Tiny;
HTTP::Tiny->new(
agent => 'AWS Perl SDK',
timeout => $self->timeout,
);
}
);

has expiration => (
is => 'rw',
isa => 'Int',
default => sub { 0 }
);

has actual_creds => (is => 'rw', default => sub { {} });

sub access_key {
my $self = shift;
$self->_refresh;
$self->actual_creds->{AccessKeyId};
}

sub secret_key {
my $self = shift;
$self->_refresh;
$self->actual_creds->{SecretAccessKey};
}

sub session_token {
my $self = shift;
$self->_refresh;
$self->actual_creds->{Token};
}

#TODO: Raise exceptions if HTTP get didn't return success
sub _refresh {
my $self = shift;

return if $self->expiration - 240 >= time;

my $ua = $self->ua;
my $r = $ua->put( $self->token_url, { headers => { 'X-aws-ec2-metadata-token-ttl-seconds' => '21600' } } );
return unless $r->{success};
return unless $r->{content};

my $token = $r->{content};
my $options = { headers => { 'X-aws-ec2-metadata-token' => $token } };

$r = $ua->get( $self->metadata_url, $options );
return unless $r->{success};
return unless $r->{content};

$r = $ua->get( $self->metadata_url . $r->{content}, $options );
return unless $r->{success};

my $json = eval { decode_json($r->{content}) };
if ($@) { die "Error in JSON from metadata URL" }

$self->actual_creds($json);
$self->expiration(DateTime::Format::ISO8601->parse_datetime($json->{Expiration})->epoch);
}

no Moose;
1;
### main pod documentation begin ###

=encoding UTF-8
=head1 NAME
Paws::Credential::InstanceProfileV2
=head1 SYNOPSIS
use Paws::Credential::InstanceProfileV2;
my $paws = Paws->new(config => {
credentials => Paws::Credential::InstanceProfile->new(
metadata_url => 'http://localhost:8000/security-credentials',
timeout => 5,
)
});
=head1 DESCRIPTION
The InstanceProfileV2 credential provider is used to call retrieve AWS credentials from instances running on AWS
When running on an instance in AWS, if said instance has a Role attached to it (also named InstanceProfile), Paws
can retrieve short-term credentials (and refresh them when needed) from the AWS provided "metadata service"
using IMDSv2 approach.
=head2 metadata_url: Str
The section in the ini file where credentials will be looked up:
=head2 metadata_url: Str
URL for client to fetch token from
=head2 timetout: Int
Number of seconds to wait before timinig out a connection to the metadata service. It defaults to 1 second, as
the metadata service is almost local, and very fast responding. Note that if set too high, and the metadata
service is not present (not running on an AWS instance), the connection has to time out
=head2 ua
A user agent that has a C<get> method. Defaults to HTTP::Tiny
=cut
6 changes: 4 additions & 2 deletions lib/Paws/Credential/ProviderChain.pm
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ package Paws::Credential::ProviderChain;
[ 'Paws::Credential::Environment',
'Paws::Credential::File',
'Paws::Credential::ECSContainerProfile',
'Paws::Credential::InstanceProfile' ]
'Paws::Credential::InstanceProfile',
'Paws::Credential::InstanceProfileV2',
]
},
);

Expand Down Expand Up @@ -62,6 +64,6 @@ It is the default provider for Paws
=head2 providers: ArrayRef[Str]
Defaults to C<[ 'Paws::Credential::Environment', 'Paws::Credential::File', 'Paws::Credential::InstanceProfile' ]>
Defaults to C<[ 'Paws::Credential::Environment', 'Paws::Credential::File', 'Paws::Credential::InstanceProfile', 'Paws::Credential::InstanceProfileV2' ]>
=cut
28 changes: 26 additions & 2 deletions t/04_credentials.t
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use Paws;
use Paws::Credential::Explicit;
use Paws::Credential::Environment;
use Paws::Credential::InstanceProfile;
use Paws::Credential::InstanceProfileV2;
use Paws::Credential::ECSContainerProfile;
use Paws::Credential::ProviderChain;
use Paws::Credential::File;
Expand Down Expand Up @@ -83,12 +84,35 @@ delete @ENV{qw(
dies_ok { $creds->access_key } 'Exception thrown when garbage arrives';
}

{
my $creds = Paws::Credential::InstanceProfileV2->new(ua => Test04::StubUAForMetadata->new(check_header => 1));
cmp_ok($creds->access_key, 'eq', 'AK1', 'Access Key 1 (IMDSv2)');
cmp_ok($creds->secret_key, 'eq', 'SK1', 'Secret Key 1 (IMDSv2)');
cmp_ok($creds->session_token, 'eq', 'TK1', 'Token 1 (IMDSv2)');

sleep 2;

cmp_ok($creds->access_key, 'eq', 'AK2', 'Access Key 2 (IMDSv2)');
cmp_ok($creds->secret_key, 'eq', 'SK2', 'Secret Key 2 (IMDSv2)');
cmp_ok($creds->session_token, 'eq', 'TK2', 'Token 2 (IMDSv2)');

sleep 2;

dies_ok { $creds->access_key } 'Exception thrown when garbage arrives (IMDSv2)';
}

{
my $creds = Paws::Credential::InstanceProfile->new(ua => Test04::StubUANoMetadata->new);

ok(not($creds->are_set), 'No Creds for no Role');
}

{
my $creds = Paws::Credential::InstanceProfileV2->new(ua => Test04::StubUANoMetadata->new);

ok(not($creds->are_set), 'No Creds for no Role (IMDSv2)');
}

{
my $creds = Paws::Credential::ECSContainerProfile->new(container_local_uri => undef);
ok(not($creds->are_set), 'Credentials are not set with no ENV var');
Expand All @@ -99,8 +123,8 @@ delete @ENV{qw(
cmp_ok($creds->metadata_url, 'eq', "http://169.254.170.2/metadata");

cmp_ok($creds->access_key, 'eq', 'AK1', 'ECS Access Key 1');
cmp_ok($creds->secret_key, 'eq', 'SK1', 'EC2 Secret Key 1');
cmp_ok($creds->session_token, 'eq', 'TK1', 'EC2 Token 1');
cmp_ok($creds->secret_key, 'eq', 'SK1', 'ECS Secret Key 1');
cmp_ok($creds->session_token, 'eq', 'TK1', 'ECS Token 1');

sleep 2;

Expand Down
4 changes: 2 additions & 2 deletions t/lib/Test04/StubUAForECSMetadata.pm
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ package Test04::StubUAForECSMetadata;

$self->increment_calls;
if ($self->calls == 1){
return { success => 1, content => '{"Code" : "Success","LastUpdated" : "2012-04-26T16:39:16Z","Type" : "AWS-HMAC","AccessKeyId" : "AK1","SecretAccessKey" : "SK1","Token" : "TK1","Expiration" : "' . DateTime->now->add(seconds => 1)->iso8601 .'Z"}' };
return { success => 1, content => '{"Code" : "Success","LastUpdated" : "2012-04-26T16:39:16Z","Type" : "AWS-HMAC","AccessKeyId" : "AK1","SecretAccessKey" : "SK1","Token" : "TK1","Expiration" : "' . DateTime->now->add(seconds => 241)->iso8601 .'Z"}' };
} elsif ($self->calls == 2){
return { success => 1, content => '{"Code" : "Success","LastUpdated" : "2012-04-26T16:39:16Z","Type" : "AWS-HMAC","AccessKeyId" : "AK2","SecretAccessKey" : "SK2","Token" : "TK2","Expiration" : "' . DateTime->now->add(seconds => 1)->iso8601 .'Z"}' };
return { success => 1, content => '{"Code" : "Success","LastUpdated" : "2012-04-26T16:39:16Z","Type" : "AWS-HMAC","AccessKeyId" : "AK2","SecretAccessKey" : "SK2","Token" : "TK2","Expiration" : "' . DateTime->now->add(seconds => 241)->iso8601 .'Z"}' };
} elsif ($self->calls == 3){
return { success => 1, content => 'Error in JSON' };
} elsif ($self->calls == 4){
Expand Down
18 changes: 15 additions & 3 deletions t/lib/Test04/StubUAForMetadata.pm
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,19 @@ package Test04::StubUAForMetadata;

has calls => (is => 'rw', isa => 'Int', default => 0, traits => ['Counter'],
handles => { increment_calls => 'inc' });

has check_headers => (is => 'ro', isa => 'Bool', default => 0);
use DateTime::Format::ISO8601;

sub get {
my ($self, $url) = @_;
my ($self, $url, $args) = @_;

if ($url eq 'http://169.254.169.254/latest/meta-data/iam/security-credentials/'){
my $valid_headers = ( $self->check_headers() ) ? $args->{headers}->{'X-aws-ec2-metadata-token'} eq 'token' : 1;
if ( $url eq 'http://169.254.169.254/latest/meta-data/iam/security-credentials/' && $valid_headers ){
return { success => 1, content => "MyRole" };
}

if ($url eq 'http://169.254.169.254/latest/meta-data/iam/security-credentials/MyRole') {
if ($url eq 'http://169.254.169.254/latest/meta-data/iam/security-credentials/MyRole' && $valid_headers ) {
$self->increment_calls;
if ($self->calls == 1){
return { success => 1, content => '{"Code" : "Success","LastUpdated" : "2012-04-26T16:39:16Z","Type" : "AWS-HMAC","AccessKeyId" : "AK1","SecretAccessKey" : "SK1","Token" : "TK1","Expiration" : "' . DateTime->now->add(seconds => 241)->iso8601 .'Z"}' };
Expand All @@ -30,4 +33,13 @@ package Test04::StubUAForMetadata;
}
die "Unknown URL in StubUA $url";
}

sub put {
my ($self, $url, $args) = @_;

if ( $url eq 'http://169.254.169.254/latest/api/token' && exists $args->{headers}->{'X-aws-ec2-metadata-token-ttl-seconds'} ) {
return { success => 1, content => 'token' };
}
die "Unknown URL in StubUA $url";
}
1;
9 changes: 9 additions & 0 deletions t/lib/Test04/StubUANoMetadata.pm
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,13 @@ package Test04::StubUANoMetadata;
return { success => 1, content => "" };
}
}

sub put {
my ($self, $url, $args) = @_;

if ( $url eq 'http://169.254.169.254/latest/api/token' && exists $args->{headers}->{'X-aws-ec2-metadata-token-ttl-seconds'} ) {
return { success => 1, content => 'token' };
}
die "Unknown URL in StubUA $url";
}
1;
2 changes: 2 additions & 0 deletions templates/default/paws_pm.tt
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,8 @@ L<Paws::Credential::File> - Gets credentials from AWS SDK config files

L<Paws::Credential::InstanceProfile> - Gets credentials from the InstanceProfile (Role) of the running instance

L<Paws::Credential::InstanceProfileV2> - Gets credentials from the InstanceProfile (Role) of the running instance using IMDSv2 approach

L<Paws::Credential::STS> - Gets temporary credentials from the Secure Token Service

L<Paws::Credential::AssumeRole> - Gets temporary credentials with AssumeRole
Expand Down

0 comments on commit 50c1d22

Please sign in to comment.