Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add metadata server imds v2 token #22

Merged
merged 1 commit into from
May 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/Credential/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ class Config

public $expiration = 0;

public $enableIMDSv2 = false;

public $metadataTokenDuration = 21600;

public function __construct($config)
{
foreach ($config as $k => $v) {
Expand Down
28 changes: 26 additions & 2 deletions src/EcsRamRoleCredential.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,35 @@ class EcsRamRoleCredential implements CredentialsInterface
*/
private $roleName;

/**
* @var boolean
*/
private $enableIMDSv2;

/**
* @var int
*/
private $metadataTokenDuration;


/**
* EcsRamRoleCredential constructor.
*
* @param $role_name
*/
public function __construct($role_name = null)
public function __construct($role_name = null, $enable_IMDS_v2 = false, $metadata_token_duration = 21600 )
{
Filter::roleName($role_name);

$this->roleName = $role_name;

Filter::enableIMDSv2($enable_IMDS_v2);

$this->enableIMDSv2 = $enable_IMDS_v2;

Filter::metadataTokenDuration($metadata_token_duration);

$this->metadataTokenDuration = $metadata_token_duration;
}

/**
Expand Down Expand Up @@ -116,7 +135,11 @@ public function getAccessKeyId()
*/
protected function getSessionCredential()
{
return (new EcsRamRoleProvider($this))->get();
$config = [
'enableIMDSv2' => $this->enableIMDSv2,
'metadataTokenDuration' => $this->metadataTokenDuration,
];
return (new EcsRamRoleProvider($this, $config))->get();
}

/**
Expand Down Expand Up @@ -148,4 +171,5 @@ public function getExpiration()
{
return $this->getSessionCredential()->getExpiration();
}

}
17 changes: 17 additions & 0 deletions src/Filter.php
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,23 @@ public static function roleName($role_name)
}
}

/**
* @param boolean|null $enable_IMDS_v2
*/
public static function enableIMDSv2($enable_IMDS_v2)
{
if (!is_bool($enable_IMDS_v2)) {
throw new InvalidArgumentException('enable_IMDS_v2 must be a string');
}
}


public static function metadataTokenDuration($metadata_token_duration) {
if (!is_int($metadata_token_duration)) {
throw new InvalidArgumentException('metadata_token_duration must be a int');
}
}

/**
* @param string $accessKeyId
* @param string $accessKeySecret
Expand Down
22 changes: 22 additions & 0 deletions src/MockTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\Psr7\Response;
use GuzzleHttp\Middleware;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;

Expand All @@ -21,6 +22,11 @@ trait MockTrait
*/
private static $mockQueue = [];

/**
* @var array
*/
private static $history = [];

/**
* @var MockHandler
*/
Expand All @@ -46,6 +52,14 @@ private static function createHandlerStack()
self::$mock = new MockHandler(self::$mockQueue);
}

/**
* @return MockHandler
*/
public static function getHandlerHistory()
{
return Middleware::history(self::$history);
}

/**
* @param string $message
* @param RequestInterface $request
Expand Down Expand Up @@ -95,4 +109,12 @@ public static function getMock()
{
return self::$mock;
}

/**
* @return array
*/
public static function getHistroy()
{
return self::$history;
}
}
93 changes: 90 additions & 3 deletions src/Providers/EcsRamRoleProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace AlibabaCloud\Credentials\Providers;

use AlibabaCloud\Credentials\Helper;
use AlibabaCloud\Credentials\Request\Request;
use AlibabaCloud\Credentials\StsCredential;
use Exception;
Expand All @@ -26,10 +27,32 @@ class EcsRamRoleProvider extends Provider
*/
protected $expirationSlot = 10;

/**
* refresh time for meta server token.
*
* @var int
*/
private $staleTime = 0;

/**
* @var string
*/
private $metadataHost = 'http://100.100.100.200';

/**
* @var string
*/
private $metadataToken;

/**
* @var string
*/
private $uri = 'http://100.100.100.200/latest/meta-data/ram/security-credentials/';
private $ecsUri = '/latest/meta-data/ram/security-credentials/';

/**
* @var string
*/
private $metadataTokenUri = '/latest/api/token';

/**
* Get credential.
Expand Down Expand Up @@ -59,6 +82,19 @@ public function get()
$result['SecurityToken']
);
}


protected function getEnableECSIMDSv2()
{
$enableIMDSv2 = Helper::envNotEmpty('ALIBABA_CLOUD_ECS_IMDSV2_ENABLE');
if ($enableIMDSv2) {
return strtolower($enableIMDSv2) === 'false' ? false : (bool)$enableIMDSv2;
}
if(isset($this->config['enableIMDSv2'])) {
return $this->config['enableIMDSv2'];
}
return false;
}

/**
* Get credentials by request.
Expand All @@ -70,13 +106,18 @@ public function get()
public function request()
{
$credential = $this->credential;
$url = $this->uri . $credential->getRoleName();
$url = $this->metadataHost . $this->ecsUri . $credential->getRoleName();

$options = [
'http_errors' => false,
'timeout' => 1,
'connect_timeout' => 1,
];

if ($this->getEnableECSIMDSv2()) {
$this->refreshMetadataToken();
$options['headers']['X-aliyun-ecs-metadata-token'] = $this->metadataToken;
}

$result = Request::createClient()->request('GET', $url, $options);

Expand All @@ -87,8 +128,54 @@ public function request()

if ($result->getStatusCode() !== 200) {
throw new RuntimeException('Error retrieving credentials from result: ' . $result->toJson());
}
}

return $result;
}

/**
* Get metadata token by request.
*
* @return ResponseInterface
* @throws Exception
* @throws GuzzleException
*/
protected function refreshMetadataToken()
{
if(!$this->needToRefresh()) {
return;
}
$credential = $this->credential;
$url = $this->metadataHost . $this->metadataTokenUri;
$tmpTime = $this->staleTime;
$this->staleTime = time() + $this->config['metadataTokenDuration'];
$options = [
'http_errors' => false,
'timeout' => 1,
'connect_timeout' => 1,
'headers' => [
'X-aliyun-ecs-metadata-token-ttl-seconds' => $this->config['metadataTokenDuration'],
],
];

$result = Request::createClient()->request('PUT', $url, $options);

if ($result->getStatusCode() != 200) {
$this->staleTime = $tmpTime;
throw new RuntimeException('Failed to get token from ECS Metadata Service. HttpCode= ' . $result->getStatusCode());
}

$this->metadataToken = $result->getBody();

return;
}


/**
* @return boolean
*/
protected function needToRefresh()
{
return \time() >= $this->staleTime;
}
}
2 changes: 2 additions & 0 deletions src/Request/Request.php
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ public static function createClient()
{
if (Credentials::hasMock()) {
$stack = HandlerStack::create(Credentials::getMock());
$history = Credentials::getHandlerHistory();
$stack->push($history);
} else {
$stack = HandlerStack::create();
}
Expand Down
19 changes: 19 additions & 0 deletions tests/Unit/CredentialTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,25 @@ public function exceptionCases()
'role_name must be a string',
],

[
[
'type' => 'ecs_ram_role',
'role_name' => 'test',
'enableIMDSv2' => 'false',
],
'enable_IMDS_v2 must be a string',
],

[
[
'type' => 'ecs_ram_role',
'role_name' => 'test',
'enableIMDSv2' => false,
'metadataTokenDuration' => 3600,
],
'metadata_token_duration must be a int',
],

[
[
'type' => 'ram_role_arn',
Expand Down
28 changes: 28 additions & 0 deletions tests/Unit/EcsRamRoleCredentialTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use InvalidArgumentException;
use PHPUnit\Framework\TestCase;
use RuntimeException;
use ReflectionClass;

class EcsRamRoleCredentialTest extends TestCase
{
Expand Down Expand Up @@ -47,6 +48,33 @@ public function testConstruct()
$this->assertEquals($expected, (string)$credential);
}

private function getPrivateField($instance, $field) {
$reflection = new ReflectionClass(EcsRamRoleCredential::class);
$privateProperty = $reflection->getProperty($field);
$privateProperty->setAccessible(true);
return $privateProperty->getValue($instance);
}

/**
* @throws GuzzleException
*/
public function testConstructWithIMDSv2()
{
// Setup
$roleName = 'role_arn';
$enableIMDSv2 = true;
$metadataTokenDuration = 3600;
$credential = new EcsRamRoleCredential($roleName, $enableIMDSv2, $metadataTokenDuration);

self::assertEquals(true, $this->getPrivateField($credential, 'enableIMDSv2'));
self::assertEquals(3600, $this->getPrivateField($credential, 'metadataTokenDuration'));

$credential = new EcsRamRoleCredential($roleName);

self::assertEquals(false, $this->getPrivateField($credential, 'enableIMDSv2'));
self::assertEquals(21600, $this->getPrivateField($credential, 'metadataTokenDuration'));
}

/**
* @throws GuzzleException
*/
Expand Down
Loading
Loading