

# Architecture details
<a name="architecture-details"></a>

 This section describes the components and AWS services that make up this solution and the architecture details on how these components work together. 

## Base module
<a name="base-module"></a>

![\[Secure Media Delivery at the Edge on AWS: Base module architecture diagram.\]](http://docs.aws.amazon.com/solutions/latest/secure-media-delivery-at-the-edge-on-aws/images/image3.png)


 The base module includes solution components that are central to the solution, while the rest of the modules further expands on it. From a functional standpoint, it encompasses some of the key elements for providing the most fundamental outcomes for this solution, which are: 
+  A validator function implemented as a CloudFront Function code that you attach to your CloudFront distribution. This function inspects requests’ attached token and validates conditions. 
+  AWS Secrets Manager stores the secrets holding signing keys that are used when the token is generated and validated. This part of the solution does not require any user maintenance when keys are created, updated new keys are distributed to CloudFront Function validator, and when keys are supplied to the service generating tokens through the provided solution library. 
+  AWS WAF rule group, Lambda function, and DynamoDB connect together to create a session revocation pipeline. The session revocation pipeline allows blocking playback sessions that are determined as compromised. The details of the compromised sessions can be either sent manually through the method that can be found in the solution library, or in an automated way by the means of automatic session revocation. Automatic session revocation automatically runs a log processing pipeline to detect suspicious sessions and push them towards that AWS WAF rule group. 

 The `Validator` CloudFront function is the same, static code which doesn’t change across implementations and use cases. As such, it can be associated with multiple CloudFront distributions delivering different types of video streaming content, as long as it is acceptable to use the same set of signing keys in all cases. Function logic takes the token (subject to validation) from the beginning of the URL path and removes it, before moving the request forward upon successfully token validation. Because CloudFront Function is attached to viewer request trigger, using an individual token does not affect the cache hit rate, as the function code removes the token from the URL path before the cache key is computed. This means that viewers requesting the same video object but using different, individual tokens, will still share the same object from the cache. With regards to scalability, CloudFront Functions have been designed to follow CloudFront scale, and no additional considerations are required in terms of adjusting the service limits for this component. When token validation process is invoked, the input string retrieved from the URL path for further processing has the following format: 

 `[SessionID].JWTHeader.JWTPayload.JWTSignagure `

 **SessionId** parameter is optional. You can generate your token without Session ID associated with it. During token generation step, if you decide to omit the Session ID, it will not be present in the URL path, and only JSON Web Token (JWT) elements will remain. In both cases, there is no need to modify CloudFront Functions as it handles the process of recognizing if Session ID is present. 

 The process of validating and processing the token can be broadly broken into three discrete stages: 
+  Verifying JWT signature to validate token integrity. The signing key used here is derived from the unique key identifier that is set in the JWT header UUID. From that UUID, CloudFront Functions determines the corresponding key value. 
+  Checking access conditions as per claims included in the token. These conditions can be categorized as follows: 
  +  Time-bounding conditions: to verify if the token is within its validity period 
  +  Asset attributes conditions: to check if requested object matches with the content the token was issued for 
  +  Viewer attributes conditions: tokens can be generated for specific viewer attributes for example, source IP, user-agent headers, session ID etc., to improve the uniqueness of a token. In this category, additional signature is calculated from the viewer’s attributes available in the function invocation context, and compared with the signature included in the token calculated at playback API stage when token was vended to the viewer. 
+  Token acceptance decision – if token verification failed in any of the previous stages, the request is terminated by the function and a 403 (Forbidden) status code is returned back to the viewer. If the request satisfies all of the access conditions and validation steps, it is permitted. After removing the token from the URL path, the request continues the normal processing path on CloudFront. 

 It is important to note that the viewer attribute conditions checks, on viewer’s request, are based on metadata included in the [event object](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/functions-event-structure.html) available in CloudFront Functions. While some of the viewer request attributes are always available and present in that object, for example, viewer’s IP, headers, and query string parameters, some of the viewer specific attributes require additional configuration steps. Those attributes are location-specific attributes that can be included in the token, for example, viewer’s country and region. This information is conveyed through specific [CloudFront generated headers](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/using-cloudfront-headers.html): CloudFront-Viewer-Country, CloudFront-Viewer-Country-Region, which are only generated and made available in the event object, when each of these headers is listed either in [cache or origin request policies](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/working-with-policies.html). Configuring these headers in the cache policy split the object in the cache per country the traffic is coming from, and effectively decrease the cache hit ratio. Therefore, we recommend including these headers in the origin request policy as additional headers. 

# Key rotation workflow
<a name="key-rotation-workflow"></a>

 Managing the signing keys used at both token generation and validation steps is a crucial token-based access control mechanism. Any change to the keys, for instance during the key rotation step, must be carefully coordinated to avoid a transient state of inconsistency. In this state, playback API and CloudFront Functions become out-of-sync, where one of these components uses a signing key that the other does not recognize. To prevent that from happening, as part of the base module an automated key rotation pipeline is built. This pipeline is initiated the first time after the base module stack is deployed, and after that periodically according to the key rotation setting specified when launching the solution. The configuration about when the key rotation process must be initiated is saved as an EventBridge rule. Any time that workflow is initiated, the subsequent steps are controlled via AWS Step Functions workflow as depicted in the following diagram. 

![\[Diagram of key rotation workflow.\]](http://docs.aws.amazon.com/solutions/latest/secure-media-delivery-at-the-edge-on-aws/images/image4.png)


 A Lambda function automatically generates a new signing key with a corresponding unique UUID value, and stores it as a temporary secret in Secrets Manager. Lambda function also makes an API call to update CloudFront Function with the newly created key and UUID. Previously used key will still be available at function runtime, so that CloudFront Function code is able to validate tokens signed both by the old and new key during the transition period when the key is rotated. 

 The next step in the Step Functions workflow is to check every minute if the new function code, including the new key, has been deployed and that it can be used to generate new keys going forward. Once confirmed, the last Lambda function in the workflow will effectively swap the keys in Secrets Manager by moving the UUID and key value stored in the primary secret to the secondary secret, and replace the primary secret with the keys that have been created and deployed in CloudFront Functions. The next time the primary secret is retrieved by the solution’s library method, a new key is used to generate the tokens. With this staged and controlled process, at any time CloudFront Functions is able to validate all incoming requests with the tokens. After new keys are deployed in the function code, there will still be an interim period before rest of Step Functions workflow updates Secrets Manager with the new key, during which time the `Playback` API service relies on the older key. Because the old key and its UUID information is still present in the function code, when a token is generated using an older key by Playback API Gateway API, CloudFront Function can recognize the UUID of the used key specified in the JWT header. When the new key is eventually fetched by `Playback` API, it will be already confirmed that the new key has also been published on CloudFront Functions side, therefore both components can rely on the new key safely. 

# Base module: Session revocation workflow
<a name="base-module-session-revocation-workflow"></a>

 The base module also comes with a series of components linked together that allows you to block any ongoing playback session based on the optional **sessionId** attribute that can be associated with the token at the time of token generation. To make use of session revocation, your playback API must include session IDs while creating the token and appended as specified before. There are two paths of emitting sessions IDs that were evaluated to be blocked - manual and automatic. Manual path provides you the flexibility in implementing custom mechanism of detecting compromised sessions while the solution provides you with an interface (in the form of library method or API endpoint) to submit the sessions you identified for blocking. For example, employing A/B watermarking type of traffic mapping and backend analysis to associate leaked stream with the session ID. To push a session ID identified in such a way, you can use a dedicated method defined in secret class, which will push that session into a dedicated DynamoDB table for this purpose, associated with the solution stack. In the automatic approach, to detect and inform about compromised sessions, an automated process is invoked at regular intervals to inspect anomalies in traffic pattern and its composition for each session, and capture the ones that deviate notably from the established norm. 

 A DynamoDB table holds the list of sessions to be processed to compile the final list of sessions that will be eventually blocked. Session blocking happens at the level of AWS WAF within the scope of the rule group created during solution deployment. You must specify the capacity of that rule group expressed in WAF capacity unit (WCU) only once before it’s created, therefore the capacity of that rule group is immutable, which will determine the upper limit of sessions that can be blocked at any time. Rules enclosed within that rule group are string matching rules, one for each blocked session, that check for a matching session ID at the beginning of the request URL path. Role of intermediary between the WAF Rule Group is assumed by a Lambda function which is initiated through DynamoDB data streams every time a change is made to the table. In addition to formulating the WAF rule group and pushing updates, a logic that orders session from the source DynamoDB table to limit the number of output sessions is implemented in a Lambda code to accommodate resulting list within the WCU limit. Refer to the following figure for a description of the logic behind filtering, ordering, and building the output list. 

![\[Diagram of session revocation workflow for Base module.\]](http://docs.aws.amazon.com/solutions/latest/secure-media-delivery-at-the-edge-on-aws/images/image5.png)


 With every change that occurs in DynamoDB, for example, adding a new session designated to be blocked, a Lambda function is invoked and all entries in the DynamoDB table are evaluated. 

 In a first step, older session IDs are filtered out before the next step to eliminate the sessions that went past retention period. Retention period is a setting defined when a stack is launched, specifying for how long a session should be kept on the blocking list when WCU limit prevents including all the sessions. After filtering the sessions past their retention timestamp, Lambda function will look into more parameters corresponding to each session to order them accordingly as follows: 

 Sessions marked as manually added take higher precedence than the ones added through automatic session revocation module. Within manual sessions, they are ordered by timestamp which equals to the time when session was added to the DynamoDB table – newest sessions have higher priority. If there is any room left (as determined by WAF rule group WCU limit), session IDs populated by the automatic session revocation module are considered and appended to the list, as reminding list size limit allows. In this category of sessions, auto revocation module adds into DynamoDB table additional parameters that allow to assess most offending sessions, that were shared with large number of unauthorized viewers. 

 This is determined by the suspicion score property, where the higher the score, the bigger the anomaly in traffic levels linked to that session. A Lambda function fills remaining slots in the list with the session IDs with the highest score. As the list is compiled, it is pushed to WAF rule group by an API call which updates entire rule group. 

 DynamoDB also includes information about the individual components that final suspicion score comprises of – `IP_Rate, IP_Penalty, referrer_penalty, UA_penalty`. This information is included to better understand what contributes to the final score and to facilitate any troubleshooting. 

 The time to live (TTL) timestamp is calculated as 24 hours from the time, entry is added to the table, determining when DynamoDB will evaluate this item as expired, and eventually deleted by the background process that continuously inspect the timestamps in this column; refer to [How it works: DynamoDB Time to Live (TTL)](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/howitworks-ttl.html). Utilizing this mechanism keeps the size of the list manageable and does not allow for sessions to accumulate in the table indefinitely. 

# API module
<a name="api-module"></a>

![\[Architecture diagram of API module.\]](http://docs.aws.amazon.com/solutions/latest/secure-media-delivery-at-the-edge-on-aws/images/image6.png)


 The API module is made available in the solution to represent an example of how to integrate token management process into the `Playback` API Gateway API section of customer architecture. The API Gateway with two Lambda integrations is the central element of this module, responsible for performing token-related operations, that is generating the token and revoking a given session ID associated with the token. The two routes defined in HTTP API Gateway configuration are: 
+  `/sessionrevoke` (allowed for POST requests) 
+  `/tokengenerate` (allowed for GET requests) 

 As incoming request matches any of the two routes, an appropriate Lambda function is invoked. Lambda function associated with `/tokengenerate` path is responsible for issuing the playback URL for a requested video asset (video asset specified as query string parameter – id). This is possible with the use of NodeJS module provided with the solution which exposes all the methods the Lambda function needs to handle token operations. However, before the token can be issued, the preceding steps must be completed in the Lambda code to collect and format the metadata about requested video asset and parameters that informs how the token must be built. In a typical production environment, customers can run a form of content management system to manage this type of required metadata. In the solution, the element is abstracted as a DynamoDB video assets table to store the necessary information for token generation. After you include the information about your video assets original (without a token) playback URL and fill in token-related parameters for each asset, Lambda code can fetch information by using video asset id that’s assigned to the metadata records in DynamoDB table and generate the token accordingly. Last element involved in token generation process is the signing key that also need to be retrieved before token generation method is called. Solution’s library has dedicated class and set of functions to obtain the keys from the Secrets Manager’s secrets defined for the launched stack. 

 The second route (`/sessionrevoke`) points to another Lambda function which parses the input of the POST request to pull the session ID and put it in the ingest DynamoDB table from the base module as an entry point for revoking the session. Session ID submitted through this path are flagged as MANUAL type in DynamoDB, which guarantees this session will be prioritized over automatically pushed session as explained in previous section. 

 You can also choose to launch a demo website comprised of few additional elements in the architecture. The demo website is made available solely to test the solution and verify if it can be successfully integrated with your video workloads. However, we strongly recommend turning off the demo website before using the solution in a production environment with sensitive video content that should be protected. This is because the demo website provides a path to interact directly with API Gateway and obtain playback tokens by unauthorized viewers, or revoke legitimate sessions. When a demo website is activated, you can verify if, as a web client, you are able to acquire playback URL with the token that would grant you permission to watch the protected content. The demo website will also decode and expose structure of the token payload making it possible to confirm if the right parameters are included. Finally, it also allows testing manual revocation of the active playback session. The demo website is comprised of static assets stored in Amazon S3 bucket created when the solution is launched. 

 Both components of API module – API and static website are exposed through the same CloudFront distribution that separates the traffic between API Gateway and S3 bucket by using CloudFront cache behaviors. For the cache behaviors linked with API Gateway: `/sessionrevoke` and `/tokengenerate`, a dedicated Lambda@Edge function is attached and invoked for every API request to sign each of the outgoing requests with SigV4 signature. It is necessary as API Gateway endpoint has IAM authorization activated to reject any token related requests without sufficient permissions as defined in IAM policies. IAM execution role configured for Lambda@Edge includes the right permissions that allows it to call the API Gateway endpoint, therefore requests signed by this function’s invocation role credentials are accepted. 

# Auto session revocation module
<a name="auto-session-revocation-module"></a>

![\[Diagram of auto session revocation module: Detect compromised sessions workflow.\]](http://docs.aws.amazon.com/solutions/latest/secure-media-delivery-at-the-edge-on-aws/images/image7.png)


 The base and API module provide the ability to react when, through external analysis, you identified a compromised playback session, if it was confirmed that session was used through unallowed distribution channels. Alternatively, you can also deploy additional and optional modules which facilitate detecting sessions with a high probability of being compromised. As the process of detecting suspicious session is run regularly, the resulting session list is emitted to DynamoDB created under base module. These sessions are ordered and processed as explained in the [Base module: session revocation workflow](auto-session-revocation-1.md) section. 

 The auto session revocation module design leverages AWS Step Functions to coordinate this entire multistep process. A predefined workflow is invoked periodically as specified in the created EventBridge rule. For ongoing video delivery streaming, set the period in the range of a few minutes to reduce the time it takes to detect and block suspicious sessions. Once workflow is initiated, the first step is to formulate the right SQL query based on the input parameters configured. The compiled SQL query is submitted as a job to Amazon Athena which initiates SQL query against the CloudFront distribution’s access logs. Bear in mind that for this process to work, session IDs must be issued for the viewers together with access tokens. Based on the provided SQL query, Athena job will eventually complete the query run, listing all the sessions that are above the acceptable suspicion score threshold, which is another input controlled by the solution’s operator. Multiple factors are taken into consideration when suspicion score level is evaluated, as explained in the Session Revocation Guide section, but put simply it is a measure of by how much request rate deviates from the median value and other signals indicative that multiple unique viewers are reusing the same session ID and token pair to watch the content simultaneously. When Athena job is completed and returned list of session is not empty, a Lambda function is invoked to ingest the session IDs with their suspicion scores and additional information, and put in DynamoDB table from the base module. 

 Before using the auto revocation module, collection of access logs to Amazon S3 must be configured for each CloudFront distribution, the traffic of which should be analyzed through this process. You must also set up a database and a table in Athena referencing access logs in the S3 bucket. 

## AWS services in this solution
<a name="aws-services"></a>


|  AWS service  |  Description  | 
| --- | --- | 
|  [Amazon CloudFront](https://aws.amazon.com/cloudfront/)  |  Core. Validates secure tokens, permitting or denying access to video content.  | 
|  [AWS Secrets Manager](https://aws.amazon.com/secrets-manager/)  |  Core. Stores secrets holding signing keys for generating and validating viewer’s tokens  | 
|  [AWS Step Functions](https://aws.amazon.com/step-functions/)  |  Core. Coordinates key rotation processes.  | 
|  [Amazon API Gateway](https://aws.amazon.com/api-gateway/)  |  Core. Processes requests to generate the tokens for video playback, and to manually revoke specified playback sessions.  | 
|  [AWS Lambda](https://aws.amazon.com/lambda/)  |  Core. Supports API Gateway to generate token for video playback and signs outgoing requests.  | 
|  [Amazon S3](https://aws.amazon.com/s3/)  |  Core. Provides storage of static assets for the demo website and CloudFront access logs.  | 
|  [Amazon DynamoDB](https://aws.amazon.com/dynamodb/)  |  Core. Stores metadata about video assets and corresponding parameters used to generate tokens.  | 
|  [Amazon EventBridge](https://aws.amazon.com/eventbridge/)  |  Core. Invokes session revocation workflow in AWS Step Functions.  | 
|  [Amazon Athena](https://aws.amazon.com/athena/)  |  Core. Runs SQL queries against CloudFront access logs to list the suspicious video playback session ids with abnormal traffic characteristics.  | 
|  [AWS WAF](https://aws.amazon.com/waf/)  |  Core. Provides the list of playback sessions that should be blocked as they get identified as compromised.  | 
|  [AWS Systems Manager](https://aws.amazon.com/systems-manager/)  |  Supporting. Provides application-level resource monitoring and visualization of resource operations and cost data.  | 