Posts Tagged rest

Python Script for AWS’ Route 53 API Authentication

[UPDATE – March 2014]

This blog post was about discovering the low level details about AWS API signature.  Additional signature methods are available today.

I would not recommend anyone to actually use the type of scripts explained below or to manually compute signatures.  You can rely on AWS SDK s to do that automatically for you (see an example here).

Nevertheless, enjoy the reading 🙂 !

[/UPDATE]

In my last post entry – Setting Up a Private VPN Server on Amazon EC2 – I end up by providing tips to rely on a fixed DNS name every time you start your server.

For the impatient : the full script is available on GitHub.  For all the others, let’s understand the problem and the proposed solution.

Why automating DNS configuration ?

This is a common problem with public cloud machines : at every start, the machine receives a different public IP address and public DNS name.  There are two methods to keep a consistent name to access your machine :

  • bundle a Dynamic DNS client (such as inadyn) and access your machine through its DynDNS domain name.
  • create a DNS A (address) or CNAME record (an alias) to point to the public IP address of your machine

The latter solution is only valid if you have your own domain name.  It offers the maximum flexibility as you can configure the DNS as you need.

To automatize the task, your DNS provider must provide you with a programmatic way to change its configuration : an API. This is exactly what Amazon’s Route 53 DNS Service offers you.

To complete my previous article, I choose to add at the end of my script a command to dynamically associate the instance public IP name to my own domain name, such as myservice.aws.stormacq.com.  I choose to define an alias to the public DNS name setup by AWS, using a CNAME record.

How to programmatically configure your Route 53 DNS ?

AWS’ Route 53 API is RESTful, making it easy to manipulate it with command line, using “curl” command for example.  curl can be used to issue GET requests to read configuration data and POST requests to change DNS configuration.

Requests payload is defined in XML.  An example GET query would be

<?xml version="1.0"?>
<ListHostedZonesResponse xmlns="https://route53.amazonaws.com/doc/2012-12-12/">
  <HostedZones>
    <HostedZone>
      <Id>/hostedzone/MY_ZONE_ID</Id>
      <Name>aws.mydomain.com.</Name>
      <CallerReference>22F684C6-3886-3FFF-8437-E22C5DCB56E7</CallerReference>
      <Config>
        <Comment>AWS Route53 Hosted subdomain</Comment>
      </Config>
      <ResourceRecordSetCount>4</ResourceRecordSetCount>
    </HostedZone>
  </HostedZones>
  <IsTruncated>false</IsTruncated>
  <MaxItems>100</MaxItems>
</ListHostedZonesResponse>

To restrict access to  your DNS configuration, the API requires authentication.  Route 53 mandate the use of a proprietary HTTP header to authenticate requests.

Full details about Route 53 API is available on Amazon’s documentation.

The problem when using authentication and curl

AWS’ Route 53 authentication is described with great details and examples in the official documentation. Basically, it is based on a HMAC signature computed from the current date/time and your AWS Secret Key.

The HTTP header to be added to the request is as following

AWS3-HTTPS AWSAccessKeyId=MyAccessKey,Algorithm=ALGORITHM,Signature=Base64( Algorithm((ValueOfDateHeader), SigningKey) )

The Algorithm can be HMacSHA1 or HMacSHA256. The date is the current system time.  You can use your system time or you can ask AWS what is their system time.  The latter needs an additional HTTP call but this method will avoid time synchronisation issues between your machine and AWS. While curl is very versatile and can accommodate to many different situations, it can not compute an HMac signature to send as authentication header, a short python script is my solution.

The Python Solution

I choose to wrap the curl call into Python, let Python compute the signature, generate the appropriate HTTP header and then call curl, passing all remaining command line arguments to curl itself. The general idea is as following :

  • collect AWS_ACCESS_KEY and AWS_SECRET_KEY
  • Compute the Signature
  • Call curl with correct parameters to inject the authentication HTTP header and all command line parameters we have received

Signature

The signature is generated with this code.  It receives two String as input (the text to sign and the key). I hard-coded the algorithm. The function returns the base64 encoded signature.

def getSignatureAsBase64(text, key):
    import hmac, hashlib, base64
    hm  = hmac.new(bytes(key, "ascii"), bytes(text, "utf-8"), hashlib.sha256)
    return base64.b64encode(hm.digest()).decode('utf-8')

AWS’s date

Retrieving AWS’s date is similarly easy

def getAmazonDateTime():
    import urllib.request
    httpResponse=urllib.request.urlopen("https://route53.amazonaws.com/date")
    httpHeaders=httpResponse.info()
    return httpHeaders['Date']

Formatting the header

And the header is formatted with

def getAmazonV3AuthHeader(accessKey, signature):
    # AWS3-HTTPS AWSAccessKeyId=MyAccessKey,Algorithm=ALGORITHM,Signature=Base64( Algorithm((ValueOfDateHeader), SigningKey) )
    return "AWS3-HTTPS AWSAccessKeyId=%s,Algorithm=HmacSHA256,Signature=%s" % (accessKey,signature)

Calling curl

Finally, we just have to call the curl command :

        import subprocess
        curlCmd = ["/usr/bin/curl",
                        "-v" if DEBUG else "",
                        "-s", "-S",
                        "--header",
                        "X-Amzn-Authorization: %s" % AWS_AUTH,
                        "--header",
                        "x-amz-date: %s" % AWS_DATE]
        curlCmd += args.curl_parameters
        curlCmd += [args.curl_url]
        logging.debug(" ".join(curlCmd))                
        return subprocess.call(curlCmd)

The full script is available under a BSD license on GitHub.  There is some additional plumbery to handle command line arguments, to load the AWS credentials etc … which is out of the scope of this article.

Conclusion

Using this script, you can easy use curl command to GET or POST REST requests to Route 53’s API.

I am using this script to create custom CNAME records whenever an EC2 instance is started, allowing me to reuse a well known, stable DNS public name to access my instance. A sample XML to define a CNAME is posted on GitHub together with the source code.

Enjoy !

, , , , , ,

4 Comments

How to deploy REST based web services to Liberty Profile ?

In my last blog entry I described how to install Liberty Profile and to configure an Eclipse based development environment.  In this entry, I will show you how to develop & deploy a “Hello World” complexity REST based web service.

Official JAX-RS / Liberty profile is available on IBM Documentation web site.  When developing or debugging REST based services, it is always good to know that IBM’s WebSphere Liberty profile is using Apache’s Wink implementation behind the scene.

Unlike some other Java based application servers (this one and this one for example), WebSphere Liberty Profile does not perform many under covers magical for you, in particular it does not register an application context, you will need to write (one line of) code to do that.

That being said, the process is quite similar for every application server and IDE :

1. Create a web based project

 

Choose a Project Name, select the runtime for deployment and uncheck the “Create EAR” option

2. add a POJO class that will serve as “resource”

Select a package name and class name.

Type the following code :

import javax.ws.rs.core.Context;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.PathParam;
import javax.ws.rs.Consumes;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.GET;
import javax.ws.rs.Produces;

@javax.ws.rs.ApplicationPath("resources")
@Path("/test")
public class Demo extends javax.ws.rs.core.Application {

    @Context
    private UriInfo context;

    @GET
    @Produces("application/xml")
    public String getXml() {
        return "<xml>Hello Rest World !</xml>";
    }

    @PUT
    @Consumes("application/xml")
    public void putXml(String content) {
    }
}

3. add your business code for the PUT and GET methods

4. Before deploying – Add JAX-RS “feature” to your server configuration

This will tell the Liberty kernel to load the JAX-RS server side implementation. You do not need to restart your server when adding / removing features.

5. Deploy and Test


At this stage, Eclipse’s browser will open on the application default URL and will display an error message.  This is normal as we did not define a landing page or default servlet in this project (index.jsp or index.html) for example.

To access the REST web service, use this URL pattern :

http://<hostname>:<port number>/<project name>/<application path>/<path>

which translates for this example to

http://localhost:9080/TestREST/resources/test

 

Et voilà, you just created, deployed and tested your first REST based web service on WebSphere Liberty Profile.

Enjoy !

 

, , , , , ,

8 Comments

Put a Java Brain into your Lego Robot

Our JavaONE session today went very well.  The room was packed (roughly 150 persons), with people standing in the back.

All demos went well, after a few surprises and tuning 🙂

Comments and questions during the talk were very positive.

As promised to the attendees, here are the slides, the source code of the REST API (as a NetBeans project) and the source code of the iPad application (as an XCode project).

Feel free to leave comments and remarks here.  Thank you for attending and enjoy !

, , , , ,

No Comments

Playing with Lego’s Mindstorms, LeJOS, GlassFish and an iPad

I will co-present three different session, hands-on-lab and BOF during JavaONE 2010.

One of these has actually nothing to do with usual business, it just aims at being fun and at playing with geek toys : we are hosting a BOF about Java programming for the Lego’s Mindstorms, i.e. the LeJOS project.

Neither David or myself are directly involved with LeJOS community, we are just regular users willing to share our experiences with people discovering Lego’s Mindstorms kit and the LeJOS project.

So, if you happen to be around in San Francisco next Wednesday, be sure to stop 45 minutes at 02:15 pm in the Parc 55 Hotel (Room Divisidero)

David will demo how to interface Lego’s NXT brick with an Arduino device.

I will show how to create a iPad controller for a robot, using a REST interface hosted on GlassFish.

Does it sounds geeky enough ? A short videoshow all this in action.

, , , , ,

No Comments