Quick and Dirty Authentication for AWS AppStream 2.0

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:

  1. A web application or a web server directory protected by basic auth (or some other method) is invoked.
  2. The user provides normal credentials to the web application or to the browser authentication prompt.
  3. 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.
  4. The API returns the pre-authenticated URL to the web application or CGI.
  5. The web application or CGI immediately redirects the user to the AppStream URL.
  6. 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:

    1
    mkdir appstream-auth
  • Create a new .htpasswd file and add accounts:

    1
    htpasswd -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.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    AuthType Basic
    AuthName "Password Required"
    AuthUserFile "/full/path/to/.htpasswd"
    Require valid-user
    <Files .ht*>
    order allow,deny
    deny 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:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      {
      "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

    1
    mkdir ~/.aws
  • In the ~/.aws directory, create a file called credentials and place entries similar to below:

    1
    2
    3
    4
    [default]
    region=us-east-1
    aws_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

    1
    composer init

    (Enter in reasonable defaults for the prompts.)

  • Install the AWS SDK for PHP

    1
    composer require aws/aws-sdk-php
  • Create the index.php file. An example is below:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    <?php
    # Load dependencies managed by Composer
    require 'vendor/autoload.php';
    # Instantiate an AWS SDK
    $sdk = new Aws\Sdk([
    'region' => 'us-east-1',
    'version' => 'latest',
    'profile' => 'default'
    ]);
    # 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.

Re-inventing WeatherPhone

A friend and colleague of mine and I were talking late last year about some of the new features Amazon Web Services debuted during the 2016 AWS re:Invent conference. Two specific items that piqued our curiosity were Amazon Polly and Amazon Lex.

Quickly, Amazon Polly is a text-to-speech engine. Give it a string of words, an optional lexicon, and it will return a stream of audio in one of several formats. Amazon Lex, on the other hand, is an engine that not only translates speech to text, but will also attempt to understand the intent of the utterance given by the user.

Given these two breakthrough technologies, there are opportunities abound. This got us thinking: what could we do with these technologies that have not been done before?

We won’t get into those specific ideas, but what I will get into is a proof of concept of what this technology can do.

The Premise

Before leaving for break, I said that I would figure out a way to demonstrate the technology. One day, sitting in front of my computer, I was looking at the weather report (I use the Currently extension in Chrome), and it dawned on me: What about those old “dial a weather report systems from yester-year?”

As Walt Disney would say, “the way to get started is to quit talking and begin doing.” And so I began…

The Pieces and Parts

To relate back to the original discussion, I knew this had to have a telephony interface. Here are the pieces and parts that make this possible:

  • Asterisk - Open source PBX platform. I happen to have one in my basement to run my phone system. While not necessary, it was good to have it connected to the PTSN so that others on the outside could test. I use Flowroute as my ITSP.
  • Node.js - Server-side Javascript Engine
  • Weather Underground API - I needed a backend to get weather forecast information. WU provides a developer limited developer account at no cost to me.

Overall Architecture

There are three major components that comprise the weatherphone application.

  1. On the far left side is Asterisk.
  2. In the middle, the weatherphone application.

    • The weatherphone application itself is a client to Asterisk. It uses a WebSocket connection to maintain an ongoing session with Asterisk
    • When weatherphone starts and opens the WebSocket, it registers itself as an application within Asterisk
    • The weatherphone app registers with a specific name (aws-polly-weatherphone)
    • Dialplans in Asterisk can reference the registered name. For example, you might see in a dialplan the following:

      1
      exten => 8000,n,Stasis(aws-polly-weatherphone)
    • The Asterisk application Stasis transfers control of the call to an ARI application (here, weatherphone).

    • Weatherphone handles incoming events from the REST API. Depending on the type of event (inbound call, DTMF tone, voice recording, etc.), weatherphone can react in numerous ways.
      • Currently weatherphone will listen for DTMF signals from the caller. Once it has 5 digits recorded for a given caller, it will send that zip code to WU for weather data.
  3. On the far right side are both AWS and Weather Underground

    • Weatherphone calls Polly on demand to translate text strings to audio output.
    • Weatherphone also calls WU in order to get current conditions and forecast data.

Current Status

The code base, which can be found on GitHub, is functional. It’s not perfect, but it does prove that an Asterisk to Polly interface works.

What’s missing is Lex. Why? Amazon Web Services hasn’t given me access to the limited preview. Until then, you have to enter in your zip code via DTMF.

Issues and Limitations

If you want to use Asterisk as your telephony interface, it has no concept of streaming audio. Everything has to be a file. This means you must run weatherphone directly on the Asterisk server, or some sort of shared storage needs to be implemented between the two services. I’ve looked around the Asterisk Wiki and forum, and the developers do not seem to think this is a priority. At best, in Asterisk 14, it is possible to playback a URI. In fact, they are still working on their own text-to-speech engine. Personally, I would rather them focus on being an excellent telephony solution, and not a text-to-speech engine.

Nitpicking a little bit, I did not give a lexicon to Polly to help it render the weather forecast. So if the wind is west-southwest, all you here is “WSW” from Polly. Or that it is “23F” (literally pronouncing the “F”). That should be fairly straightforward to fix.

Obviously the code I wrote isn’t production ready whatsoever. It does what it needs to do to prove a point, but it is buggy and not optimal by any stretch. Proceed at your own peril!

Wrap-up

Overall, this proves that is possible to integrate a cloud-based text-to-speech engine with a popular telephony solution. As soon as I can get access to Lex, I will continue improving this code base so that you can ask weatherphone specific questions and it attempt to give you a relevant answer.

With those pieces together, imagine the possibilities!

Get the code here: https://github.com/seliger/weatherphone

Google Home -- Seems Underwhelming at the Moment

Since my brother-in-law and his wife are living with us for a few weeks while “the woods” is being finished, they have let me play around with their Google Home. It was a struggle from the get-go.

Off to a Poor Start

First and foremost, if you’ve set your Google Home up elsewhere and expect to plug it in at someone else’s house and run their gear, you’re looking at the wrong device. Once we finally got it connected to my WiFi, it wouldn’t talk to any of my devices.

Of course, we did things the hard way and did a factory reset by holding down the mute button for 10 seconds. Once it reset, I reconnected to the WiFi and MY Google account. Suddenly it was able to see my devices. I found this a bit strange as anyone who can access my network can see my other Chromecast devices without any sort of additional authentication. Why the Google Home can’t, I haven’t a clue.

Can You Hear Me Now?

I have a VIZIO Crave 360 speaker that I received for Christmas. It sounds great. It’s even cute because I can put the Google Home and the Crave 360 into an audio group and cast to them simultaneously. Unfortunately, the Crave 360 shows how (relatively speaking) the Home’s speaker is inferior. Don’t get me wrong, for it’s size, it does an acceptable job. However, compared to the Crave 360, the sound is muddy and muffled.

Also, even with music playing, it is hard to hear Google at times. It will lower the music on itself, but not on any other output devices. Furthermore, if you already had the Home turned down low, it doesn’t speak up so you can hear it. I guess that’s great if you don’t want to wake up the baby in the next room over, but for me, I just want to hear what she’s trying to tell me. (More on that in a moment.)

Still a Toddler in Understanding Commands

This was the most gut wrenching part… One would assume that the smart folks at Google would want to cram pack as much functionality as possible. However, I find myself woefully disappointed in what I cannot do with the device. In the couple of hours of messing around with it, I quickly found these several things horribly broken:

  • In Google Play Music, it has no idea how to play auto-playlists. For example, I really like to listen to the variety in my “Thumbs Up” list. No such luck. It has no idea about this supposed playlist.
  • I cannot tell Google Home that I like a song. I would tell Google, “I like this song,” with the expectation that it would automatically add it to my Thumbs Up list (I suppose I shouldn’t be surprised given the bullet above…). How did I work around this? I told my phone to listen to the current track, tell me what it was, and go to it in the Google Play Music app. How backwards is that?
  • As a joke, I wanted to text my wife who was off in another room. Google quickly responded, “I don’t know how to do that yet.” Seriously? My phone has done that for years…
  • We were running the fireplace last night. I like to run the furnace fan to circulate the air throughout. While Google Home can control my Nest, it had no idea what it meant when I told it, “run the fan for an hour.” There’s certainly an option to do that in the Nest App…
  • Offhandedly, I told it simply to cast Youtube to the livingroom TV. It found some random TED talk about homosexuality and started casting it. This was only slightly awkward with my 4 and 7 year olds in the room who don’t quite yet understand those aspects of life yet.
  • If you are not using the Gmail app, it does not sync your calendar. For example, I have been a long-time (well before Gmail supported Exchange Active-Sync) user of Nine, and while it integrates with the Google calendar on my phone, Google Home has no idea of anything on my agenda. Pity.

I’m not alone. Corbin Davenport over at Android Police reported similar disappointments just yesterday. He made the comparisons between the Pixel, Google Now, and the lacking Google Home. In the comments people also put together the fact that Google rather keep halfway re-inventing the wheel, rather than just presenting one strong platform. Interesting theory.

Novel, but Not Necessary…

Don’t get me wrong. The Google Home is certainly cute, but for me it is a novelty. It needs to know more about the world it came from (e.g. Google an it’s plethora of platforms…). I feel like they spent more time teaching Google Home how to tell terrible jokes, rather than how to use the network of platforms it has connected to it, that we as humans are used to using.

I really want to like it, because I tend to be a gadget guy. However, this one is not sitting well with me. I’d rather go buy another Crave 360 and have tunes simulcast throughout my house.

© 2017 Corey Seliger All Rights Reserved.
Theme by hiero