1

I'm trying to serve time-limited links to private content on a Cloudfront-enabled Amazon S3 bucket.

I would like to be able to use the AWS PHP API

$credentials = array("key" => $variables->strAmazonAccessKey, "secret" => $variables->strAmazonSecretKey);
$s3 = new AmazonS3($credentials);
$cdn = new AmazonCloudFront($credentials);

$cdn->set_keypair_id($variables->cdn_keypair_id);
$cdn->set_private_key($variables->cdn_private_key);

$response = $s3->set_object_acl($bucket, $obj, AmazonS3::ACL_OWNER_FULL_CONTROL);
return htmlspecialchars($cdn->get_private_object_url($cloudfront_id, $obj, '1 day'));

But I keep getting this access denied message

<Error>
<Code>AccessDenied</Code>
<Message>Access Denied</Message>
<RequestId>BD2B4CE946ED67C4</RequestId>
<HostId>
JrBu9+HqhGwzRA4ILFeT2SGyp5nXEY/RrYWQDz2dzdWDIRTgVy2i3Llm460ok99M
</HostId>
</Error>

The cdn_private_key is a string containing the RSA private key which looks sort of like this:

-----BEGIN RSA PRIVATE KEY-----
ldflsjhflasdkjfhlvbdslvahsdlviwuheliuhvlhlvweljbsdvljkbsadvlkjsd
ldflsjhflasdkjfhlvbdslvahsdlviwuheliuhvlhlvweljbsdvljkbsadvlkjsd
ldflsjhflasdkjfhlvbdslvahsdlviwuheliuhvlhlvweljbsdvljkbsadvlkjsd
ldflsjhflasdkjfhlvbdslvahsdlviwuheliuhvlhlvweljbsdvljkbsadvlkjsd
ldflsjhflasdkjfhlvbdslvahsdlviwuheliuhvlhlvweljbsdvljkbsadvlkjsd
ldflsjhflasdkjfhlvbdslvahsdlviwuheliuhvlhlvweljbsdvljkbsadvlkjsd
ldflsjhflasdkjfhlvbdslvahsdlviwuheliuhvlhlvweljbsdvljkbsadvlkjsd
ldflsjhflasdkjfhlvbdslvahsdlviwuheliuhvlhlvweljbsdvljkbsadvlkjsd
ldflsjhflasdkjfhlvbdslvahsdlviwuheliuhvlhlvweljbsdvljkbsadvlkjsd
ldflsjhflasdkjfhlvbdslvahsdlviwuheliuhvlhlvweljbsdvljkbsadvlkjsd
ldflsjhflasdkjfhlvbdslvahsdlviwuheliuhvlhlvweljbsdvljkbsadvlkjsd
ldflsjhflasdkjfhlvbdslvahsdlviwuheliuhvlhlvweljbsdvljkbsadvlkjsd
ldflsjhflasdkjfhlvbdslvahsdlviwuheliuhvlhlv=
-----END RSA PRIVATE KEY-----

I may be doing something wrong there, but I would've expected to get an error about the key or signature instead of an access denied message.

I have also tried manually signing using the following method, but get the same error, only with different HostId and RequestId:

$accessId = $variables->cdn_keypair_id;
$priv_key = $variables->cdn_private_key;
$resource = 'https://'. $cloudfront_id .'/' . $obj;

$expires = time() + 3600*24; 
$to_sign = '{"Statement":[{"Resource":"'.$resource.'","Condition":{"DateLessThan":{"AWS:EpochTime":'.$expires.'}}}]}';     

$signature = '*Signature will go here*'; 
$pkeyid = openssl_get_privatekey($priv_key); 
if (openssl_sign( $to_sign, $signature, $pkeyid, OPENSSL_ALGO_SHA1)) { 
    $signature = urlencode( base64_encode( $signature ) ); 
} 
return ($resource.'?Key-Pair-Id='.$accessId.'&Expires='.$expires.'&Signature='.$signature); 
1
  • This is really stating the obvious but, I really hope you're not actually using the private key posted here. Commented Sep 26, 2014 at 14:21

1 Answer 1

3

I've eventually solved the problem.

It seems that Amazon aren't very clear about this ... hidden deep in the bowels of AWS documentation you are instructed to set the bucket permissions on the S3 bucket to allow CloudFront access to it.

Further confusion ensues when you have to set the Principal property on the policy, as it suggests you need to get the Canonical User ID. However, this is NOT the Canonical User ID for your AWS account found on the Security Credentials page .. it is instead found in the "Origin Access Identity" link on the CloudFront console.

Here is how to do it ....

First create/obtain the CloudFront Origin Access Identity like this:-

$oai_id = $cdn->list_oais()->body->CloudFrontOriginAccessIdentitySummary->Id;
if(!$oai_id)
{
    $cdn->create_oai('SOME_IDENTIFIER');
    $oai_id = $cdn->list_oais()->body->CloudFrontOriginAccessIdentitySummary->Id;
}

Now apply the policy to the bucket to allow CloudFront access:-

$cuid = $cdn->get_oai($oai_id)->body->S3CanonicalUserId;
$policy = new CFPolicy($s3, array(
    'Statement' => array(
        array( // Statement #1
            'Sid' => 'AddPerm',
            'Effect' => 'Allow',
            'Principal' => array(
                'CanonicalUser' => "$cuid"
            ),
            'Action' => array('s3:GetObject'),
            'Resource' => array('arn:aws:s3:::'.$bucket.'/*')
        )
    )
));
// Set the bucket policy
$response = $s3->set_bucket_policy($bucket, $policy);
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.