question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Presign URL for AWS IoT https / mqtt client

See original GitHub issue

Hi!

Trying to get a websockets from web-browsers to work through AWS IoT by presigning a URL which is delivered to the web-browser/client but it seems there is no way for boto3 to presign a URL with the method “GET” which is the method used to access IoT Websockets (as far as i understand at least)

Code:

import boto3
sts = boto3.client('sts')
creds = sts.assume_role(RoleArn="arn:aws:iam::000000000000:role/service-role/test", RoleSessionName="web-client-some-ip")
iot = boto3.client(
        'iot',
        aws_access_key_id=creds['Credentials']['AccessKeyId'],
        aws_secret_access_key=creds['Credentials']['SecretAccessKey'],
        aws_session_token=creds['Credentials']['SessionToken'])
print(iot.generate_presigned_url('GET', ExpiresIn=3600))

Traceback:

Traceback (most recent call last):
  <flask traceback dropped>
  File "/home/oskar/Code/project/web.py", line 96, in get_live_feed
    print(iot.generate_presigned_url('GET', ExpiresIn=3600))
  File "/home/oskar/venv/lib/python3.6/site-packages/botocore/signers.py", line 563, in generate_presigned_url
    raise UnknownClientMethodError(method_name=client_method)
botocore.exceptions.UnknownClientMethodError: Client does not have method: GET

According to the docs it is possible to presign url’s for https connections to AWS IoT gateway, but there seems be something missing in boto to support it.

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Comments:9 (2 by maintainers)

github_iconTop GitHub Comments

3reactions
cetexcommented, Feb 8, 2018

Sure, just pasting it here.

Of course all ‘\n’ were reinterpreted as actual newlines so i had to fix that manually, I hope i didn’t miss something more.

import hmac, datetime, urllib.parse, hashlib
def aws_sign(key, msg):
    return hmac.new(key, msg.encode('utf-8'), hashlib.sha256).digest()

def aws_getSignatureKey(key, dateStamp, regionName, serviceName):
    kDate = aws_sign(('AWS4' + key).encode('utf-8'), dateStamp)
    kRegion = aws_sign(kDate, regionName)
    kService = aws_sign(kRegion, serviceName)
    kSigning = aws_sign(kService, 'aws4_request')
    return kSigning

def aws_presign(access_key=None, secret_key=None, session_token=None, host=None, region=None, method=None, protocol=None, uri=None, service=None, expires=3600, payload_hash=None):
    # method=GET, protocol=wss, uri=/mqtt service=iotdevicegateway
    assert 604800 >= expires >= 1, "Invalid expire time 604800 >= %s >= 1" % expires;

    # Date stuff, first is datetime, second is just date.
    t = datetime.datetime.utcnow()
    date_time = t.strftime('%Y%m%dT%H%M%SZ')
    date = t.strftime('%Y%m%d')
    # Signing algorithm used
    algorithm = 'AWS4-HMAC-SHA256'

    # Scope of credentials, date + region (eu-west-1) + service (iot gateway hostname) + signature version
    credential_scope = date + '/' + region + '/' + service + '/' + 'aws4_request'
    # Start building the query-string
    canonical_querystring = 'X-Amz-Algorithm=' + algorithm
    canonical_querystring += '&X-Amz-Credential=' + urllib.parse.quote_plus(access_key + '/' + credential_scope)
    canonical_querystring += '&X-Amz-Date=' + date_time
    canonical_querystring += '&X-Amz-Expires=' + str(expires)
    canonical_querystring += '&X-Amz-SignedHeaders=host'

    if payload_hash is None:
        if service == 'iotdevicegateway':
            payload_hash = hashlib.sha256(b'').hexdigest()
        else:
            payload_hash = 'UNSIGNED-PAYLOAD'

    canonical_headers = 'host:' + host + '\n'
    canonical_request = method + '\n' + uri + '\n' + canonical_querystring + '\n' + canonical_headers + '\nhost\n' + payload_hash

    string_to_sign = algorithm + '\n' + date_time + '\n' + credential_scope + '\n' + hashlib.sha256(canonical_request.encode()).hexdigest()
    signing_key = aws_getSignatureKey(secret_key, date, region, service)
    signature = hmac.new(signing_key, string_to_sign.encode('utf-8'), hashlib.sha256).hexdigest()

    canonical_querystring += '&X-Amz-Signature=' + signature
    if session_token:
        canonical_querystring += '&X-Amz-Security-Token=' + urllib.parse.quote(session_token)

    return protocol + '://' + host + uri + '?' + canonical_querystring
1reaction
claudiopastorinicommented, Jan 21, 2020

Did you see the below failure after while connecting to the aws IoT instance ? UnicodeError: encoding with ‘idna’ codec failed

Did anybody solve this? It seems related to the length of the URI.

UnicodeError: encoding with 'idna' codec failed (UnicodeError: label too long)

This is my really simple example with paho-mqtt:


import paho.mqtt.client as mqtt

import urllib.parse

def on_connect(mqttc, obj, flags, rc):
    print("rc: "+str(rc))

def on_message(mqttc, obj, msg):
    print(msg.topic+" "+str(msg.qos)+" "+str(msg.payload))

def on_publish(mqttc, obj, mid):
    print("mid: "+str(mid))

def on_subscribe(mqttc, obj, mid, granted_qos):
    print("Subscribed: "+str(mid)+" "+str(granted_qos))

def on_log(mqttc, obj, level, string):
    print(string)

mqttc = mqtt.Client(transport="websockets")
mqttc.on_message = on_message
mqttc.on_connect = on_connect
mqttc.on_publish = on_publish
mqttc.on_subscribe = on_subscribe

url = "YOUR_PRESIGNED_URL_STARTING_WITH_WSS_HERE"

mqttc.connect(url, 443, 60)
mqttc.subscribe("test/iot", 0)

mqttc.loop_forever()
Read more comments on GitHub >

github_iconTop Results From Across the Web

Managing jobs - AWS IoT Core - AWS Documentation
Presigned URLs. Your job document can contain a presigned Amazon S3 URL that points to your code file (or other file). Presigned Amazon...
Read more >
Generate a presigned request with the WebSocket library
The following shows how you to generate a presigned request so that you can use the ... If your URL doesn't include the...
Read more >
Sample Python code to generate presigned URL
The following code shows an example for generating the pre-signed URL using Python as the programming language. Pre-requisites.
Read more >
PresignedUrlConfig - AWS IoT
Pre-signed URLs are generated when Jobs receives an MQTT request for the job document. Type: Long. Valid Range: Minimum value of 60. Maximum...
Read more >
Connecting to AWS IoT Core by using custom authentication
The following example demonstrates how to pass credentials through the HTTP Upgrade request. GET /mqtt HTTP/1.1 Host: your-endpoint Upgrade ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found