AWSNinja Code Library
Source: at GitHub
In a Sentence: Setting up Private Streaming on CloudFront is hard, so I automated it.
Setting up private streaming on CloudFront is way more difficult that it probably needs to be. There are all sorts of little niggling details that will have you pulling your hair out. My hope is that this post will help ease the pain, by gliding you right past the rough edges so you can get it done and maintain your sanity.
To do that, this tutorial will set up a CloudFront Streaming Distribution, set up the security, upload some videos, and set up a webpage to view those videos using private streaming. In addition to the AWS Ninja code that I’ve written, the package includes the JW Player and the AWS SDK for PHP, both of which are included consistent with the licenses granted by those works.
If you’re just trying to learn more about the architecture of CloudFront and the API, you might just want to take a look at the source of oneTimeSetup.php, pushFilesToCloudFront.php, and cleanup.php. The steps for signing requests are in index.php.
Despite these limitations, CloudFront Private Streaming does give you a lot. It prevents unauthorized streaming of your content by IP address of the user and/or date range. This allows you to effectively set up a pay-walled or limited access video site or intranet for content you do not want to make available to the world. Also, CloudFront is one of the AWS services that is still in beta, and we can reasonably expect Amazon to add these and other missing security features at some point.
You should also keep in mind, nothing is 100% secure. It’s a truism that Anybody who can watch your video can steal your video. Your objective is to employ cost-effective means of making your content hard to steal.
If you decide that CloudFront does meet your requirements, what follows is your guide to go from zero to streaming.
First, download the awsninja_cloudfrontprivatestreaming project from GitHub. The project also includes a copy of the newly-relaunched AWS SDK for PHP from Amazon and the JW Player flash player. Once you’ve downloaded and uncompressed the library, you need to set up the SDK for PHP config. Rename the “./aws/sdk/config-sample.inc.php” file to “”./aws/sdk/config.inc.php.” Then, specify all of the parameters that are listed there:
Instructions for how to set all of these can be found in the “config.inc.php” file itself.
Once you’ve completed the SDK config file, you should then run the “oneTimeSetup.php” script on the command line. This is found in the “examples” directory. This script automates and ensures completion of all of the steps of the setup process. Here are the pitfalls the script helps you avoid:
The “oneTimeSetup.php” script will take up to 15 minutes to run because it takes that long for your new Streaming Distribution to become active and deployed. Once that completes successfully, rename the “config-sample.php” file to “config.php.” The “oneTimeSetup.php” script will echo out the PHP define() methods that you need to put in the “config.php” file (not the AWS SDK “config.inc.php” file!). This completes the configuration of the other scripts.
To see that your Streaming Distribution is all set up, log into the AWS Console and click the CloudFront tab:
Now that your CloudFront Origin bucket, OAI, and Streaming Distribution are all set up and configured, it’s time to upload some videos to your streaming bucket.
Anyway, the “getCrockfordVids.sh” file will download Crockford videos to the “video” directory (warning: the five files that are set to download by default comprise 1.6GB of video). Once that is complete, you can run the “pushFilesToCloudFront.php” script to upload the files to the S3 bucket that was created for you by the “oneTimeSetup.php” script. In this script, there are two calls to the S3 API. The first one simply puts the object to S3, specifying that the owner has full control:
echo("Put $file on S3\n"); $cbr = $s3->create_object(NINJA_STREAMING_BUCKET, $file, array( 'fileUpload'=>NINJA_BASEPATH . 'awsninja_cloudfrontprivatestreaming/videos/' . $file, 'acl'=>AmazonS3::ACL_OWNER_FULL_CONTROL ));
The second step sets the Access Control List (ACL) so that the Origin Identity we previously set up has READ access. This is the step that allows your CloudFront Distribution to read your files from S3.
$acl = array( array( 'id'=>AWS_CANONICAL_ID, 'permission'=>AmazonS3::GRANT_FULL_CONTROL ), array( 'id'=>NINJA_STREAMING_CANONICALID, 'permission'=>AmazonS3::GRANT_READ ) ); echo("Update the ACL for $file on S3.\n\n"); $sas = $s3->set_object_acl(NINJA_STREAMING_BUCKET, $file, $acl);
Note that the $acl array of arrays holds the permissions for the owner (indicated by the AWS_CANONICAL_ID constant) and the Origin Access Identity (indicated by NINJA_STREAMING_CANONICALID).
Next, you need to set up the webpage that will be used for streaming. All of the files you need to stream to are in the “www” directory. If your project is not in your web root, you will need to either move the “www” directory (and fix the include paths in “index.php”) or use a symbolic link so that you can view that directory in your web browser.
Extending the AmazonCloudFrontClass
In order to make this tutorial work, there is a minor change that needed to be made to the AmazonCloudFront service class in the SDK for PHP. The original AmazonCloudFront service contains a method called get_private_object_url() which is designed to create the signed url requests that are used to serve private content. The method returns a complete signed url with domain name, path and query string. This is a problem because JW Player actually wants the domain name and the path as two separate arguments. To solve this, I extended the AmazonCloudFront class from the SDK and added a method called get_private_object_path() which just consists of the portion of the get_private_object_url() which generates the path. The get_private_object_url() remains the same (except that the logic that was moved to get_private_object_path() was replaced with a call to the new method). You can inspect this work in the “ninja.cloudfront.class.php” file.
There are a few different options for protecting your stream. At a minimum, you need to set an expiration date (called “DateLessThan”) after which the stream stops working. Additionally, you can set a start date (“DateGreaterThan”) and/or an IP Address. If you set the IPAddress, the request must come from that IP Address in order to work.
If you want to make sure that your content can only be streamed from a web page that you control, the best way to do that is to sign the request using the $_SERVER['REMOTE_ADDR'] PHP global. Your arguments in “index.php” would look like this:
$expiration = strtotime('+4320 minutes'); //three days $opt = array( // 'BecomeAvailable'=>strtotime('+10 minutes'), //starts working in 10 minutes 'IPAddress'=> $_SERVER['REMOTE_ADDR'] //limit to the current IP address (prevents the user from embedding the flash player to serve the content to other users) ); $cfsn = new AmazonCloudFrontNinja(); $urlcloud = $cfsn->get_private_object_path($item, $expiration, $opt);;
Note: “BecomeAvailable” is the argument you send to the AmazonCloudFront service (the SDK for PHP). Inside the service class, it gets changed to “DateGreaterThan” which is the correct name for the CloudFront API. Since the “BecomeAvailable” parameter is commented out in the code above, the stream would be available immediately.
Pull up the “index.php” file in your web browser - and you should see your videos. Happy New Year!
Once you’re done working though this tutorial, the “cleanup.php” script will remove all of the Streaming Distributions and OAIs. The process of removing these objects is nearly as complicated as creating themm but I an not going to describe it. The source code has documentation and explains what’s going on. Take Heed: If you’re currently running a live Streaming Distribution, this script will delete that one too.