diff --git a/includes/classes/Snapshots/DynamoDBConnector.php b/includes/classes/Snapshots/DynamoDBConnector.php index f5d08e4..9d3a8ff 100644 --- a/includes/classes/Snapshots/DynamoDBConnector.php +++ b/includes/classes/Snapshots/DynamoDBConnector.php @@ -9,6 +9,8 @@ use Aws\DynamoDb\DynamoDbClient; use Aws\DynamoDb\Marshaler; +use Aws; +use TenUp\Snapshots\Exceptions\SnapshotsException; /** * Class for handling Amazon dynamodb calls @@ -212,18 +214,34 @@ public function delete_snapshot( string $id, string $profile, string $repository */ private function get_client( string $profile, string $region ) : DynamoDbClient { $client_key = $profile . '_' . $region; + $role_arn = $_ENV['role_arn']; $args = [ 'region' => $region, - 'profile' => $profile, 'version' => '2012-08-10', 'csm' => false, ]; - // Check if the necessary AWS env vars are set; if so, the profile arg is not needed. - // These are the same env vars the SDK checks for in CredentialProvider.php. - if ( getenv( 'AWS_ACCESS_KEY_ID' ) && getenv( 'AWS_SECRET_ACCESS_KEY' ) ) { - unset( $args['profile'] ); + // if role_arn has a value use STS to assume the role + // and pass the credential info to DynamoDbClient later on + if ( $role_arn != "" ) { + $args['roleArn'] = $role_arn; + + $temporaryCredentials = $this->assumeRole($args); + + if ( ! is_array( $temporaryCredentials ) ) { + throw new SnapshotsException( sprintf( "Failed to assume role '%s'.", $args['roleArn'] ) ); + } + + $args['credentials'] = [ + 'key' => $temporaryCredentials['AccessKeyId'], + 'secret' => $temporaryCredentials['SecretAccessKey'], + 'token' => $temporaryCredentials['SessionToken'] + ]; + } + + if ( $role_arn == "" && $profile != "" ) { + $args['profile'] = $profile; } if ( ! isset( $this->clients[ $client_key ] ) ) { @@ -232,4 +250,23 @@ private function get_client( string $profile, string $region ) : DynamoDbClient return $this->clients[ $client_key ]; } + + /** + * Performs STS + * + * @param string $role_arn AWS role_arn + */ + private function assumeRole( $connectionParameters ) : array { + $stsClient = new Aws\Sts\StsClient([ + 'region' => 'us-east-1', + 'version' => '2011-06-15' + ]); + + $result = $stsClient->AssumeRole([ + 'RoleArn' => $connectionParameters['roleArn'], + 'RoleSessionName' => "wpsnapshots", + ]); + + return $result['Credentials']; + } } diff --git a/includes/classes/Snapshots/S3StorageConnector.php b/includes/classes/Snapshots/S3StorageConnector.php index 877414b..7230857 100644 --- a/includes/classes/Snapshots/S3StorageConnector.php +++ b/includes/classes/Snapshots/S3StorageConnector.php @@ -10,6 +10,7 @@ use Aws\S3\S3Client; use TenUp\Snapshots\Exceptions\SnapshotsException; use TenUp\Snapshots\SnapshotsDirectory; +use Aws; /** * Class S3StorageConnector @@ -232,19 +233,35 @@ public function test( string $profile, string $repository, string $region ) { */ private function get_client( string $profile, string $region ) : S3Client { $client_key = $profile . '_' . $region; + $role_arn = $_ENV['role_arn']; $args = [ - 'region' => $region, - 'profile' => $profile, + 'region' => $region, 'signature' => 'v4', 'version' => '2006-03-01', - 'csm' => false, + 'csm' => false, ]; - // Check if the necessary AWS env vars are set; if so, the profile arg is not needed. - // These are the same env vars the SDK checks for in CredentialProvider.php. - if ( getenv( 'AWS_ACCESS_KEY_ID' ) && getenv( 'AWS_SECRET_ACCESS_KEY' ) ) { - unset( $args['profile'] ); + // if role_arn has a value use STS to assume the role + // and pass the credential info to S3Client later on + if ( $role_arn != "" ) { + $args['roleArn'] = $role_arn; + + $temporaryCredentials = $this->assumeRole($args); + + if ( ! is_array( $temporaryCredentials ) ) { + throw new SnapshotsException( sprintf( "Failed to assume role '%s'.", $args['roleArn'] ) ); + } + + $args['credentials'] = [ + 'key' => $temporaryCredentials['AccessKeyId'], + 'secret' => $temporaryCredentials['SecretAccessKey'], + 'token' => $temporaryCredentials['SessionToken'] + ]; + } + + if ( $role_arn == "" && $profile != "" ) { + $args['profile'] = $profile; } if ( ! isset( $this->clients[ $client_key ] ) ) { @@ -254,6 +271,25 @@ private function get_client( string $profile, string $region ) : S3Client { return $this->clients[ $client_key ]; } + /** + * Performs STS + * + * @param string $role_arn AWS role_arn + */ + private function assumeRole( $connectionParameters ) : array { + $stsClient = new Aws\Sts\StsClient([ + 'region' => 'us-east-1', + 'version' => '2011-06-15' + ]); + + $result = $stsClient->AssumeRole([ + 'RoleArn' => $connectionParameters['roleArn'], + 'RoleSessionName' => "wpsnapshots", + ]); + + return $result['Credentials']; + } + /** * Get bucket name * diff --git a/includes/classes/SnapshotsConfig/SnapshotsConfigFromFileSystem.php b/includes/classes/SnapshotsConfig/SnapshotsConfigFromFileSystem.php index 1d37410..6998fe4 100644 --- a/includes/classes/SnapshotsConfig/SnapshotsConfigFromFileSystem.php +++ b/includes/classes/SnapshotsConfig/SnapshotsConfigFromFileSystem.php @@ -130,10 +130,26 @@ public function get_repository_profile( string $repository = '' ) : string { $settings = $this->get_repository_settings( $repository ); if ( is_array( $settings ) ) { - return $settings['profile'] ?? 'default'; + return $settings['profile'] ?? ''; } - return 'default'; + return ''; + } + + /** + * Gets the roleArn property from a repository. Defaults to ''. + * + * @param string $repository Repository name. + * @return string $profile Profile name. + */ + public function get_repository_role_arn( string $repository = '' ) : string { + $settings = $this->get_repository_settings( $repository ); + + if ( is_array( $settings ) ) { + return $settings['roleArn'] ?? ''; + } + + return ''; } /** diff --git a/includes/classes/SnapshotsConfig/SnapshotsConfigInterface.php b/includes/classes/SnapshotsConfig/SnapshotsConfigInterface.php index 297647e..db79f72 100644 --- a/includes/classes/SnapshotsConfig/SnapshotsConfigInterface.php +++ b/includes/classes/SnapshotsConfig/SnapshotsConfigInterface.php @@ -56,6 +56,14 @@ public function get_repository_region( string $repository ) : ?string; */ public function get_repository_profile( string $repository = '' ) : ?string; + /** + * Gets the roleArn property from a repository. + * + * @param string $repository Repository name. + * @return ?string $roleArn Role ARN string. + */ + public function get_repository_role_arn( string $repository = '' ) : ?string; + /** * Gets repositories. * diff --git a/includes/classes/WPCLI/WPCLICommand.php b/includes/classes/WPCLI/WPCLICommand.php index 870591d..ae2aaf3 100644 --- a/includes/classes/WPCLI/WPCLICommand.php +++ b/includes/classes/WPCLI/WPCLICommand.php @@ -283,6 +283,9 @@ protected function get_profile_for_repository() : string { $profile = $this->config->get_repository_profile( $repository_name ); + // FIXME: hack to get the role_arn available later for demo purposes + $_ENV['role_arn'] = $this->config->get_repository_role_arn( $repository_name ); + return $profile; }