3
votes

I want to deploy my code to an EC2 instance, but I don't want to bake in the AWS Key and Secret. AWS Provides an IAM service to allow me to assign a role to my EC2 instances which will allow those instances access using temporary keys.

How do I get this to work? I have tried using the SDK and StsClient to assumeRole but this throws up an error

User:  arn:aws:sts::XXXXXXXXXXXX:assumed-role/ROLE-NAME/INSTANCE No is not authorized to perform: sts:AssumeRole on resource: arn:aws:iam::XXXXXXXXXXXX:role/ROLE-NAME

Reading around and it seems that the STS request needs to use credentials itself to perform the Role assignment? but I thought that the EC2 instance keys would be used?

1
Correct -- Temporary credentials (eg as provided in EC2 metadata) cannot be used to create other temporary credentials. You will need to use 'permanent' credentials associated with a User.John Rotenstein
Incorrect, please see my answer below. You use the temporary instance credentials to authenticate against a role, which then assigns you a temporary set you can use to access the service.Graeme
Sorry, you are indeed right! The documentation says "You cannot call AssumeRole by using AWS root account credentials; access is denied. You must use credentials for an IAM user or an IAM role to call AssumeRole." Thanks for showing the clever method!John Rotenstein
It had me scratching my head for hours, as having to have an account essentially makes it a useless feature so I knew it had to be possible. You're welcome, hopefully this page will come in useful for others until the docs become clearer at least :)Graeme

1 Answers

6
votes

Yes the STS call to assume role DOES need credentials to work, however it uses the credentials provided when it is constructed, if these are omitted it doesn't fall back to instance based credentials.

To use instance based credentials to make the call you need to use something like :

$credentials = \Aws\Credentials\CredentialProvider::instanceProfile();

Which will fetch the name of the associated role, and then the temp instance credentials. If you know the name of the role you can specify it in the constructor

$credentials = \Aws\Credentials\CredentialProvider::instanceProfile(['profile' => 'role-name-here']);

You will also need to update the Trust Relationship on the role to allow the instance to assume the role. I assumed the Service entry would do this however changing this to reference the IAM role seems to work

{
  "Effect": "Allow",
  "Principal": {
    "AWS": "arn:aws:iam::XXXXXXXXXXXX:role/ROLE-NAME"
  },
  "Action": "sts:AssumeRole"
}

(Don't forget to add the comma on the end of the previous sections closing } )

Now pass those credentials into the constructor of the Sts client and your call to AssumeRole should return successfully. My test code for this is:

$credentials = \Aws\Credentials\CredentialProvider::instanceProfile();

$stsClient = new \Aws\Sts\StsClient(['region' => 'eu-west-1', 'version' => 'latest', 'credentials' => $credentials]);

$ar = $stsClient->assumeRole(['RoleArn' => 'arn:aws:iam::XXXXXXXXXXXX:role/Ec2Role-queue', 'RoleSessionName' => 'test']);

$creds = $stsClient->createCredentials($ar);

var_dump($creds);

Which outputs :

object(Aws\Credentials\Credentials)#96 (4) {
  ["key":"Aws\Credentials\Credentials":private]=>
  string(20) "XXXXIYH36RJ5NZCDXXXX"
  ["secret":"Aws\Credentials\Credentials":private]=>
  string(40) "eXXXX+azLUNi9LjwyX4MkNI4rnEpFrG9pNNXXXXX"
  ["token":"Aws\Credentials\Credentials":private]=>
  string(308)     "FQoDYXdzEH4aDIa3Rx/onWIa4ArZeyLHAX+muL7zKt9trAQhMa98pkzpGGmOGa0N5UhCjX2GXQ3Dc2APElwlpCfr9F+J2k5igAeonadgrwAOC/OvEDv34i1JdmkaUjEE14S2hVGz2dXXXXegYra7kvx0cdoOjCPIFmXSZJeD1PR27lFyacH2x5+F1XKFugveiYCD63axATp4t8fq0K+EPjXXXX/wYKm5tJt7hYkCV7+tThLYFDPZ6NkXXXXjsSKkOw9u52yGJY4yD50y+liSprHH+/ZJyQppDIJcZbbpyBoojoeRvwU="
  ["expires":"Aws\Credentials\Credentials":private]=>
  int(1474580894)
}

Hopefully that helps someone else save a few hair follicles :)