I recently ran into a situation where AWS AppStream 2.0 might be a viable choice for securely delivering a desktop application required for an important pilot project. I won’t go into details, but the server-side of the pilot is already being hosted in AWS, so it only made sense to attempt to find a solution within that platform.
With that said, the first sticky wicket became authentication. AppStream 2.0 supports two kinds of authentication:
- Federated login using SAML
- Short Duration URLs
It so happens that we do have full support for SAML via Shibboleth. However, the process to provsion and configure it may be overkill for a short term pilot with a limited number of users. That immediately means considering short duration URLs.
Short duration URLs (or temporary URLs) are just that. They are a pre-authenticated URL generated by a trusted source and expire after a certain amount of time. AppStream 2.0 provides an API that generates these URLs. They can have a lifetime of one minute all the way through one day. Given the secure nature of this app, it is prudent to use pre-authenticated URLs for the shortest duration possible, and attempt to reduce the visibility of that URL as well.
In this scenario, imagine the process looking like this:
- A web application or a web server directory protected by basic auth (or some other method) is invoked.
- The user provides normal credentials to the web application or to the browser authentication prompt.
- Because we have successfully authenticated a trusted user, the web app or custom crafted CGI script calls the CreateStreamingURL API. It passes the user’s login name to the API so that AppStream 2.0 can keep track of who is whom.
- The API returns the pre-authenticated URL to the web application or CGI.
- The web application or CGI immediately redirects the user to the AppStream URL.
- The user is presented with the AppStream interface, ready to be used.
The rest of this post will detail a methdology for implementing such an interface. This will be as vanilla as possible, using only basic technologies to achive the desired outcome. You can take from this example and build a more robust solution in your own environments.
Key Assumptions
- Overall solution being implemented on a UNIX/Linux variant
- Using Apache httpd
- Using basic authentication using an .htaccess file, and an htpasswd file to control access to the protected resource
- The code in charge of generating the short duration URL and redirecting the user will be written in PHP
- Composer will be used to install the AWS SDK for PHP
This assumes that all of the aforementioned items are installed and that you have them configured in a suitable way. It is outside the scope of this post to detail web server and PHP configuration. Furthermore, if you are building this into your solution, take the appropriate liberties of inferencing what I am doing and make it fit properly.
Last, but certainly not least, this assumes that you already have an AppStream stack configured, its subsequent fleet is running, and that the API will properly generate streaming URLs. You can validate this by selecting your stack and selecting “Create streaming URL” from the Actions menu inside the AWS console.
Phase 1: Configuring the Secure Site
Within your web server tree, create a new folder:
1mkdir appstream-authCreate a new .htpasswd file and add accounts:
1htpasswd -c .htpasswd <username>(Drop the “-c” if you intend on appending additional users)
Create an .htaccess file to protect the site. Below is a sample that uses the aforementioned .htpasswd file.
123456789AuthType BasicAuthName "Password Required"AuthUserFile "/full/path/to/.htpasswd"Require valid-user<Files .ht*>order allow,denydeny from all</Files>
Phase 2: Create a Restricted User within AWS
You will need to create a restricted user that has the capability of invoking the CreateStreamingURL API. You will do this on the Identity and Access Management (IAM) tool within the AWS Console.
Use the “Create Polcy” button to create a new policy
Assign it a name (e.g. appstream_createStreamURL)
Use the following policy:
123456789101112{"Version": "2012-10-17","Statement": [{"Effect": "Allow","Action": ["appstream:createStreamingURL"],"Resource": "*"}]}Note: I use Resource: “*“ purely for convenience. Feel free to restrict this down to the stacks you intend to expose via this account.
Use the “Add User” button to create the new user
- For Access Type, ensure to only check Programmatic Access and do not generate a password that can be used to login to the web console
- Associate the policy you created in the previous step. (Either do this via a group, or by direct policy association.)
Phase 3: Configure the AWS Credential File
You will need to configure a credentials file on your system using the previously created credentials so that the AWS SDK for PHP will know how to properly connect to the AWS API endpoint.
Create the directory for the credentials file
1mkdir ~/.awsIn the
~/.aws
directory, create a file calledcredentials
and place entries similar to below:1234[default]region=us-east-1aws_access_key_id=<access key id>aws_secret_access_key=<secret access key>
Phase 4: Develop the Redirector
Inside the web app directory, run Composer to initialize PHP dependency management
1composer init(Enter in reasonable defaults for the prompts.)
Install the AWS SDK for PHP
1composer require aws/aws-sdk-phpCreate the index.php file. An example is below:
123456789101112131415161718192021222324252627282930# Load dependencies managed by Composerrequire 'vendor/autoload.php';# Instantiate an AWS SDK$sdk = new Aws\Sdk(['region' => 'us-east-1','version' => 'latest','profile' => 'appstream']);# Instantiate an AppStream client# *** NOTE: The following line is invoking a workaround. See explanation# below the snippet.$appstream = $sdk->createAppstream(['endpoint' => 'https://appstream2.us-east-1.amazonaws.com/']);# Generate the short duration URL$url = $appstream->createStreamingURL(['StackName' => 'sublime_text','FleetName' => 'sublime_text-fleet','UserId' => $_SERVER['PHP_AUTH_USER']]);# Trigger the redirect to the short duration URL.header('Location: '.$url['StreamingURL'])
NOTE: While assembling this overview, I apparently stumbled upon a bug in the AWS PHP SDK. The createStreamingURL() funciton is a part of AppStream 2.0, but the PHP SDK attempts to use the legacy API endpoint, causing issues. To work around this issue, I have forced the API to use the correct endpoint. I have no idea if this breaks the rest of the API calls related to AppStream. I do know it works for this example, therefore it will stand. You can follow the issue here on GitHub.
Summary
Obviously this is overly simplified. However, it shows how easy it could be to integrate AWS Appstream 2.0 into your applications. Not sure if we will use this for our pilot project, but I feel better knowing that we have an option available to us if it is necessary.