-
Notifications
You must be signed in to change notification settings - Fork 33
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add AWS IAM Authentication strategy (#61)
- Loading branch information
Showing
4 changed files
with
255 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
120 changes: 120 additions & 0 deletions
120
src/AuthenticationStrategies/AwsIamAuthenticationStrategy.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
<?php | ||
|
||
namespace Vault\AuthenticationStrategies; | ||
|
||
use Aws\Middleware; | ||
use Aws\Sts\StsClient; | ||
use Psr\Http\Message\RequestInterface; | ||
use Psr\Http\Client\ClientExceptionInterface; | ||
use Vault\Exceptions\AuthenticationException; | ||
use Vault\ResponseModels\Auth; | ||
|
||
/** | ||
* Class AwsIamAuthenticationStrategy | ||
* | ||
* @package Vault\AuthenticationStrategy | ||
*/ | ||
class AwsIamAuthenticationStrategy extends AbstractAuthenticationStrategy | ||
{ | ||
/** | ||
* @var string | ||
*/ | ||
protected $methodPathSegment = 'aws'; | ||
|
||
/** @var string */ | ||
private $role; | ||
|
||
/** @var string */ | ||
private $region; | ||
|
||
/** @var ?string */ | ||
private $serverId; | ||
|
||
/** | ||
* @param string $role The name of the vault role | ||
* @param string $region The AWS region to use | ||
* @param ?string $vaultServerId If set, this string is used as X-Vault-AWS-IAM-Server-ID, to protect against replay attacks | ||
* @param ?StsClient $stsClient Custom instance of StsClient | ||
*/ | ||
public function __construct( | ||
$role, | ||
$region, | ||
$vaultServerId = null, | ||
$stsClient = null | ||
) { | ||
$this->role = $role; | ||
$this->region = $region; | ||
$this->serverId = $vaultServerId; | ||
$this->stsClient = $stsClient; | ||
} | ||
|
||
/** | ||
* Returns auth for further interactions with Vault. | ||
* | ||
* @return Auth | ||
* @throws AuthenticationException | ||
* @throws ClientExceptionInterface | ||
*/ | ||
public function authenticate(): Auth | ||
{ | ||
if (!$this->methodPathSegment) { | ||
throw new AuthenticationException('methodPathSegment must be set before usage'); | ||
} | ||
|
||
if (!$this->stsClient) { | ||
$this->stsClient = new StsClient([ | ||
'region' => $this->region, | ||
'version' => 'latest', | ||
'sts_regional_endpoints' => 'regional', | ||
]); | ||
} | ||
|
||
|
||
// Creating a signed command, to get the parameters for the actual login-request to vault | ||
$command = $this->stsClient->getCommand('GetCallerIdentity'); | ||
|
||
if ($this->serverId) { | ||
$command->getHandlerList()->appendBuild( | ||
Middleware::mapRequest(function (RequestInterface $request) { | ||
return $request->withHeader('X-Vault-AWS-IAM-Server-ID', $this->serverId); | ||
}), | ||
'add-header' | ||
); | ||
} | ||
|
||
$request = \Aws\serialize($command); | ||
|
||
$response = $this->client->write( | ||
sprintf('/auth/%s/login', $this->methodPathSegment), | ||
[ | ||
'role' => $this->role, | ||
'iam_http_request_method' => $request->getMethod(), | ||
'iam_request_url' => base64_encode($request->getUri()), | ||
'iam_request_body' => base64_encode($request->getBody()), | ||
'iam_request_headers' => base64_encode(json_encode($request->getHeaders())), | ||
] | ||
); | ||
|
||
return $response->getAuth(); | ||
} | ||
|
||
/** | ||
* @return string | ||
*/ | ||
public function getMethodPathSegment(): string | ||
{ | ||
return $this->methodPathSegment; | ||
} | ||
|
||
/** | ||
* @param string $methodPathSegment | ||
* | ||
* @return static | ||
*/ | ||
public function setMethodPathSegment(string $methodPathSegment) | ||
{ | ||
$this->methodPathSegment = $methodPathSegment; | ||
|
||
return $this; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
|
||
- | ||
request: | ||
method: POST | ||
url: 'http://127.0.0.1:8200/v1/auth/aws/login' | ||
headers: | ||
Host: '127.0.0.1:8200' | ||
Expect: null | ||
Accept-Encoding: null | ||
User-Agent: VaultPHP/1.0.0 | ||
Content-Type: application/json | ||
Accept: null | ||
body: '{"role":"dev-role","iam_http_request_method":"POST","iam_request_url":"aHR0cHM6Ly9zdHMuZXUtY2VudHJhbC0xLmFtYXpvbmF3cy5jb20=","iam_request_body":"QWN0aW9uPUdldENhbGxlcklkZW50aXR5JlZlcnNpb249MjAxMS0wNi0xNQ==","iam_request_headers":"eyJIb3N0IjpbInN0cy5ldS1jZW50cmFsLTEuYW1hem9uYXdzLmNvbSJdLCJDb250ZW50LUxlbmd0aCI6WyI0MyJdLCJDb250ZW50LVR5cGUiOlsiYXBwbGljYXRpb25cL3gtd3d3LWZvcm0tdXJsZW5jb2RlZCJdLCJYLUFtei1Vc2VyLUFnZW50IjpbImF3cy1zZGstcGhwXC8zLjI2MC4zIE9TXC9EYXJ3aW5cLzIyLjEuMCBsYW5nXC9waHBcLzguMC4yNyJdLCJVc2VyLUFnZW50IjpbImF3cy1zZGstcGhwXC8zLjI2MC4zIE9TXC9EYXJ3aW5cLzIyLjEuMCBsYW5nXC9waHBcLzguMC4yNyJdLCJYLVZhdWx0LUFXUy1JQU0tU2VydmVyLUlEIjpbImxvY2FsaG9zdCJdLCJhd3Mtc2RrLXJldHJ5IjpbIjBcLzAiXX0="}' | ||
response: | ||
status: | ||
http_version: '1.1' | ||
code: '200' | ||
message: OK | ||
headers: | ||
Cache-Control: no-store | ||
Content-Type: application/json | ||
Date: 'Tue, 01 Aug 2017 06:54:33 GMT' | ||
Content-Length: '366' | ||
body: "{\"request_id\":\"08962062-3aab-fd89-3413-99c3521ecc75\",\"lease_id\":\"\",\"renewable\":false,\"lease_duration\":0,\"data\":null,\"wrap_info\":null,\"warnings\":null,\"auth\":{\"client_token\":\"9e64c3b8-01f7-7a64-1575-30a91f7d1ae4\",\"accessor\":\"033ccada-d332-9415-0acc-0ec51e81dbd1\",\"policies\":[\"default\",\"test\"],\"metadata\":{\"username\":\"test\"},\"lease_duration\":2764800,\"renewable\":true}}\n" | ||
- | ||
request: | ||
method: GET | ||
url: 'http://127.0.0.1:8200/v1/auth/token/lookup-self' | ||
headers: | ||
Host: '127.0.0.1:8200' | ||
Accept-Encoding: null | ||
User-Agent: VaultPHP/1.0.0 | ||
Content-Type: application/json | ||
X-Vault-Token: 9e64c3b8-01f7-7a64-1575-30a91f7d1ae4 | ||
Accept: null | ||
response: | ||
status: | ||
http_version: '1.1' | ||
code: '200' | ||
message: OK | ||
headers: | ||
Cache-Control: no-store | ||
Content-Type: application/json | ||
Date: 'Tue, 01 Aug 2017 06:54:33 GMT' | ||
Content-Length: '504' | ||
body: "{\"request_id\":\"ab3f3dc9-3633-0c18-44e2-f3f58cb56d0a\",\"lease_id\":\"\",\"renewable\":false,\"lease_duration\":0,\"data\":{\"accessor\":\"033ccada-d332-9415-0acc-0ec51e81dbd1\",\"creation_time\":1501570473,\"creation_ttl\":2764800,\"display_name\":\"userpass-test\",\"explicit_max_ttl\":0,\"id\":\"9e64c3b8-01f7-7a64-1575-30a91f7d1ae4\",\"meta\":{\"username\":\"test\"},\"num_uses\":0,\"orphan\":true,\"path\":\"auth/userpass/login/test\",\"policies\":[\"default\",\"test\"],\"renewable\":true,\"ttl\":2764800},\"wrap_info\":null,\"warnings\":null,\"auth\":null}\n" |
87 changes: 87 additions & 0 deletions
87
tests/unit/AuthenticationStrategies/AwsIamAuthenticationStrategyTest.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
<?php | ||
|
||
use Aws\Sts\StsClient; | ||
use Codeception\Test\Unit; | ||
use Psr\Http\Client\ClientExceptionInterface; | ||
use Vault\AuthenticationStrategies\AwsIamAuthenticationStrategy; | ||
use Vault\Client; | ||
use VCR\VCR; | ||
use Laminas\Diactoros\RequestFactory; | ||
use Laminas\Diactoros\StreamFactory; | ||
use Laminas\Diactoros\Uri; | ||
|
||
class AwsIamAuthenticationStrategyTest extends Unit | ||
{ | ||
/** | ||
* @var UnitTester | ||
*/ | ||
protected $tester; | ||
|
||
/** | ||
* @throws \Psr\Cache\InvalidArgumentException | ||
* @throws ClientExceptionInterface | ||
* @throws \Vault\Exceptions\RuntimeException | ||
*/ | ||
public function testCanAuthenticate(): void | ||
{ | ||
$client = new Client( | ||
new Uri('http://127.0.0.1:8200'), | ||
new \AlexTartan\GuzzlePsr18Adapter\Client(), | ||
new RequestFactory(), | ||
new StreamFactory() | ||
); | ||
|
||
$stsClient = new StsClient([ | ||
'region' => 'eu-central-1', | ||
'version' => 'latest', | ||
'sts_regional_endpoints' => 'regional' | ||
]); | ||
// These middlewares would break the test, due to some dynamic headers | ||
$stsClient->getHandlerList()->remove('invocation-id'); | ||
$stsClient->getHandlerList()->remove('signer'); | ||
|
||
$client->setAuthenticationStrategy( | ||
new AwsIamAuthenticationStrategy( | ||
'dev-role', | ||
'eu-central-1', | ||
'localhost', | ||
$stsClient | ||
) | ||
); | ||
|
||
$this->assertEquals($client->getAuthenticationStrategy()->getClient(), $client); | ||
$this->assertTrue($client->authenticate()); | ||
$this->assertNotEmpty($client->getToken()); | ||
$this->assertNotEmpty($client->getToken()->getAuth()->getLeaseDuration()); | ||
$this->assertNotEmpty($client->getToken()->getAuth()->isRenewable()); | ||
} | ||
|
||
protected function setUp(): void | ||
{ | ||
VCR::turnOn(); | ||
VCR::configure()->setMode(VCR::MODE_ONCE); | ||
|
||
VCR::insertCassette('authentication-strategies/aws-iam'); | ||
|
||
parent::setUp(); | ||
} | ||
|
||
protected function tearDown(): void | ||
{ | ||
// To stop recording requests, eject the cassette | ||
VCR::eject(); | ||
|
||
// Turn off VCR to stop intercepting requests | ||
VCR::turnOff(); | ||
|
||
parent::tearDown(); | ||
} | ||
|
||
protected function _before() | ||
{ | ||
} | ||
|
||
protected function _after() | ||
{ | ||
} | ||
} |