Coverage for snapcraft/plugins/awsiot : 0%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*- # # Copyright (C) 2015 Canonical Ltd. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License version 3 as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>.
"""A plugin to configure an AWS IoT thing and setup for MQTT certs
The AWS IoT feature requires registration and configuration to communicate with the EC2 cloud. This plugin has configuration options for many of these settings, and some it can generate if there isn't an appropriate value to get. It will provide a basis for any software designed to communicate with the EC2 IoT interfaces.
- generatekeys (string) True if new keys should be generated by Amazon, otherwise generate keys locally - policydocument (string) Which policy document should be used. Optional. A doc which allows all IoT will be used if not specified - policyname (string) Which policy name should be used. Optional. 'PubSubToAnyTopic' will be used if not specified - thing (string) The thing to create - endpoint (string) AWS Endpoint to use if non-default
"""
import os import snapcraft import urllib.request import os.path import json import subprocess
class AWSIoTPlugin(snapcraft.BasePlugin):
@classmethod def schema(cls): return { '$schema': 'http://json-schema.org/draft-04/schema#', 'type': 'object', 'properties': { 'generatekeys': { 'type': 'boolean', 'default': True }, 'policydocument': { 'type': 'string', 'default': '' }, 'policyname': { 'type': 'string', 'default': 'PubSubToAnyTopic' }, 'thing': { 'type': 'string', }, 'endpoint': { 'type': 'string', }, }, 'required': ['thing'] }
def __init__(self, name, options): super().__init__(name, options) self.build_packages.append('awscli') self.aws = ['aws', 'iot']
if (options.endpoint): self.aws.extend(['--endpoint', options.endpoint])
def pull(self): return True
def run_to_file(self, cmds, filename): output = self.run_output(cmds, cwd=self.builddir) with open(filename, 'w') as f: f.write(output)
def _keys_from_aws(self, certsdir): # generate new keys self.run_to_file(self.aws + ['create-keys-and-certificate', '--set-as-active'], os.path.join(certsdir, 'certs.json')) # separate into different files with open(os.path.join(certsdir, 'certs.json')) as data_file: certsjson = json.load(data_file)
with open(os.path.join(certsdir, 'cert.pem'), 'w') as text_file: text_file.write(self.data['certificatePem']) with open(os.path.join(certsdir, 'privateKey.pem'), 'w') \ as text_file: text_file.write(self.data['keyPair']['PrivateKey']) with open(os.path.join(certsdir, 'publicKey.pem'), 'w') \ as text_file: text_file.write(self.data['keyPair']['PublicKey'])
return certsjson
def _keys_from_local(self, certsdir): certsjson = None
# generate private key csr = os.path.join(certsdir, 'cert.csr') self.run(['openssl', 'genrsa', '-out', os.path.join(certsdir, 'privateKey.pem'), '2048']) self.run(['openssl', 'req', '-new', '-key', os.path.join(certsdir, 'privateKey.pem'), '-out', csr])
# generate new keys based on a csr certresp = os.path.join(self.builddir, 'certresponse.txt') self.run_to_file(self.aws + ['create-certificate-from-csr', '--certificate-signing-request', csr, '--set-as-active'], certresp) with open(certresp) as data_file: certsjson = json.load(data_file)
self.run_to_file(self.aws + ['describe-certificate', '--certificate-id', self.data['arn'].split(':cert/')[1], '--output', 'text', '--query', '{}Description.{}Pem'.format( 'certificate') ], os.path.join(certsdir, 'cert.pem'))
return certsjson
def build(self): certsdir = os.path.join(self.builddir, 'certs') # Make the certs directory if it does not exist os.makedirs(certsdir, exist_ok=True)
# What should we do with certificates? if self.options.generatekeys: self.data = self._keys_from_aws(certsdir) else: self.data = self._keys_from_local(certsdir)
# Extra check, but good to ensure if self.data is None: return
# Get the root certificate self.filename = urllib.request.urlretrieve( 'https://www.symantec.com/content/en/us/enterprise/verisign/roots/' 'VeriSign-Class%203-Public-Primary-Certification-Authority-G5.pem', filename=os.path.join(certsdir, 'rootCA.pem'))
# attach policy to certificate if self.options.policydocument is not '': self.pd = ('{\n' ' "Version": "2012-10-17",\n' ' "Statement": [{\n' ' "Effect": "Allow",\n' ' "Action":["iot:*"],\n' ' "Resource": ["*"]\n' ' }]\n' '}\n' ) self.options.policydocument = "policydocument" with open(self.options.policydocument, "w") as text_file: text_file.write(self.pd)
arnresp = os.path.join(self.builddir, 'arnresponse.txt') try: self.run_to_file(self.aws + ['create-policy', '--policy-name', self.options.policyname, '--policy-document', 'file://' + self.options.policydocument], arnresp) except subprocess.CalledProcessError: print("If the policy name already exists ' \ 'then creating it will fail. You can ignore this error.") self.run_to_file(self.aws + ['get-policy', '--policy-name', self.options.policyname], arnresp)
with open('arnresponse.txt') as data_file: self.data = json.load(data_file)
self.run(self.aws + ['attach-principal-policy', '-‐principal-arn', self.data["policyArn"], '--policy-name', self.options.policyname])
self.run(self.aws + ['create-thing', '--thing-name', self.options.thing])
print("Created Thing: %s" % self.options.thing)
def stage_fileset(self): fileset = super().stage_fileset() fileset.append('-certresponse.txt') fileset.append('-arnresponse.txt') return fileset |