PCG logo
Article

How to use AWS Systems Manager Parameters with only cURL and OpenSSL

Sometimes your project may need to communicate with AWS Application Programming Interface (Amazon API) without any access to AWS Command Line InterfaceExternal Link (AWS CLI) or Software Development KitsExternal Link (AWS SDK). For me it was the moment when I had to extract AWS Systems ManagerExternal Link (AWS SSM) Parameters from inside a DockerExternal Link container running in Amazon Elastic Container ServiceExternal Link (Amazon ECS) on Amazon Elastic Compute CloudExternal Link (Amazon EC2) Instance. The container was quite tiny and had no AWS CLI on it. Base image used was busyboxExternal Link and it was making the situation even more complicated. Installing AWS CLI would mean bringing in PythonExternal Link with all its dependencies — not an option. The only solution left was to communicate with Amazon API via Representational state transfer (REST) with cURL.

The issue (and the reason why I am writing this post) was, that there are not many tutorials on the Internet on how to do that with cURLExternal Link. Moreover, there are no posts on how it works with AWS EC2 instance profileExternal Link credentials (the ones we inherit from the Amazon EC2 instance) and nothing on how to do all of that for AWS SSM APIExternal Link. Below, I will briefly go through all the steps and highlight the details you should know. In the end, I will attach the script that performs the whole process.

Prerequisites:

  1. Your AWS Identity and Access ManagementExternal Link (IAM) user/IAM instance profileExternal Link of your Amazon EC2 instance must have IAM policyExternal Link that allows executing “ssm:getparameters”.
  2. Your environment must have cURL and OpenSSLExternal Link command line tools installed.

In order to send requests to the Amazon API via REST, you have to sign them to make identifiable. The signing of signature version 4External Link (the one AWS uses on almost all its services in 2019) generally contains the next fours steps:

  1. Generate canonical request
  2. Use it to create a string to sign
  3. Generate signing key, create signature based on the key and the string to sign
  4. Put the signature as part of the Hypertext Transfer ProtocolExternal Link (HTTP) request (e.g. as an HTTP header or a query string parameter)

Step 1: Canonical Request

First of all, you need to specify the information from your request such as HTTP method, headers, payload, etc. in the precise order described in the pseudo-code snippet 1. When you are running the script with help of instance profile credentials (as I did) you have to add an additional header x-amz-security-token:< your-token > to it. The security token as well, as access and secret keysExternal Link can be derived from the instance metadata endpointExternal Link from within the Instance. Note: in the < SignedHeaders > you must specify the headers in the same order you do in < CanonicalHeaders >.

Code Copied!copy-button
$HTTPMethod
$CanonicalURI

content-type:application/x-amz-json-1.1
host:$AwsService.$MyRegion.amazonaws.com
x-amz-date:$MyDateAndTime
x-amz-security-token:$Token
x-amz-target:$Target

content-type;host;x-amz-date;x-amz-security-token;x-amz-target
$HashedPostData

Snippet 1. Example of canonical request in Bash. Empty lines and newlines must be preserved

In most tutorials about signing process, authors use Amazon Simple Storage ServiceExternal Link (Amazon S3) API to list objects as an example. The difference is that AWS SSM GetParameterExternal Link expects POST method instead of GET (more about itExternal Link), and therefore you need to provide a proper hashed (JSONExternal Link) payload to it. Make sure that you have a well-formatted JSON string and do not try to hash a JSON file instead. For hashing payload, you can use OpenSSL.

Step 2: String to Sign

Next step is creating the string to sign, which you will eventually turn into a valid signature. For that you need the hashed canonical request (from step 1), hashing algorithm (AWS4-HMAC-SHA256), timestamp and scope in format < yyyyMMdd >/< Region >/< Service >/aws4_request. In the snippet 2 below you can see the order you should use for it.

Code Copied!copy-button
AWS4-HMAC-SHA256
$MyDateAndTime
$MyDate/$MyRegion/$AwsService/aws4_request
$CanonicalRequestHash

Snippet 2. Example Bash code for a string to sign

Step 3: Create Signature

Now you need to calculate the signing key and use it to create the signature. The signing key is derived through four-step HMAC-SHA256 hashing. It starts with hashing of your secret access key with date, then it hashes the result with region, then with AWS service name and possibly with “aws4_request”, which is a constant string. After that, you need to hash the string to sign from the previous step with the signing key you just derived. This will be your signature. The step is displayed on snippet 3.

Code Copied!copy-button
dateKey=$(hmac_sha256 key:"AWS4$SecretAccessKey" $MyDate)
dateRegionKey=$(hmac_sha256 hexkey:$dateKey $MyRegion)
dateRegionServiceKey=$(hmac_sha256 hexkey:$dateRegionKey 
$AwsService)
HexKey=$(hmac_sha256 hexkey:$dateRegionServiceKey "aws4_request")

Snippet 3. Template for a signing key and signature creation

Step 4: Build HTTP Request

Finally you have everything you need to make the request. You only need to wrap it in cURL command and execute it. You have to specify all canonical headers from step 1, add authorisation header and plain JSON payload. Authorisation header consists of hashing algorithm, credentials, signed headers (step 1) and the signature from step 4. The snippet 5 is a cURL command with all flags configured to send the request to AWS SSM API.

Code Copied!copy-button
curl -vvv https://$AwsService.$MyRegion.amazonaws.com/ \
   -X $HTTPMethod \
   -H "Authorization: AWS4-HMAC-SHA256 \
      Credential=$AccessKeyId/$MyDate/$MyRegion/$AwsService/aws4_request, \
      SignedHeaders=content-type;host;x-amz-date;x-amz-security-token;x-amz-target, \
      Signature=$HashSignature" \
   -H "x-amz-security-token: $Token" \
   -H "x-amz-target: $Target" \
   -H "content-type: application/x-amz-json-1.1" \
   -d $RequestPostData \
   -H "x-amz-date: $MyDateAndTime"

Snippet 4. cURL command for AWS SSM API

If you did everything right, you will finally see HTTP/1.1 200 OK response.

Summary

As you can see, the process can be characterised as “hashing and mixing the same set of inputs in different ways”, and that’s what makes it look so complicated. I published the scriptExternal Link on GitHub; it allows, with little to no changes' communication with most AWS APIs. If you have problems with it or any other topic-related thing, please do not hesitate to leave questions in comments and I will do my best to answer it.




Services Used

Continue Reading

Article
AWS Lambda: Avoid these common pitfalls

It's a great offering to get results quickly, but like any good tool, it needs to be used correctly.

Learn more
Case Study
Financial Services
Cloud Migration
The VHV Group's Cloud Journey - Strategy for Success

How does an insurance company with more than 4,000 employees balance compliance, modernization, and cost efficiency?

Learn more
Case Study
Financial Services
DevOps
A KYC Archival System for a Digital Bank

Building a KYC archival Cloud platform for a digital bank to store customers’ KYC data.

Learn more
Case Study
Software
DevOps
Accounting Accelerates

What began as a start-up in their parents' basement has developed into a leading provider of cloud-based accounting and financial software within just a few years: sevDesk.

Learn more
See all

Let's work together

United Kingdom
Arrow Down