Skip to content

Commit f2d5c65

Browse files
authored
Fleet provisioning docs (#294)
* add detailed fleet provisioning instructions, make small formate changes, and add python file to parse cert * make changes specific to c++ * fix arguemnt tags * format command line instructions into a more readable form * fix file path and add comment explaining where to run command * explicitly use python3 * explicitly use python3
1 parent a724f2a commit f2d5c65

File tree

2 files changed

+260
-79
lines changed

2 files changed

+260
-79
lines changed

samples/README.md

Lines changed: 191 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ The terminal prompts the user for input. Type something and press enter to publi
4040
Since the sample is subscribed to the same topic, it will also receive the message back from the server.
4141
Type `quit` and press enter to end the sample.
4242

43-
Source: `samples/mqtt/basic_pub_sub`
43+
Source: `samples/mqtt/basic_pub_sub/main.cpp`
4444

4545
Your Thing's
4646
[Policy](https://docs.aws.amazon.com/iot/latest/developerguide/iot-policies.html)
@@ -102,82 +102,7 @@ This sample is similar to the Basic Pub-Sub, but the connection setup is more ma
102102
This is a starting point for using custom
103103
[Configurable Endpoints](https://docs.aws.amazon.com/iot/latest/developerguide/iot-custom-endpoints-configurable.html).
104104

105-
source: `samples/mqtt/raw_pub_sub`
106-
107-
## Fleet provisioning
108-
109-
This sample uses the AWS IoT
110-
[Fleet provisioning](https://docs.aws.amazon.com/iot/latest/developerguide/provision-wo-cert.html)
111-
to provision devices using either a CSR or KeysAndcertificate and subsequently calls RegisterThing.
112-
113-
On startup, the script subscribes to topics based on the request type of either CSR or Keys topics,
114-
publishes the request to corresponding topic and calls RegisterThing.
115-
116-
Source: `samples/identity/fleet_provisioning`
117-
cd ~/aws-iot-device-sdk-cpp-v2-build/samples/identity/fleet_provisioning
118-
119-
Run the sample like this to provision using CreateKeysAndCertificate:
120-
121-
``` sh
122-
./fleet-provisioning --endpoint <endpoint> --ca_file <path to root CA>
123-
--cert <path to the certificate> --key <path to the private key>
124-
--template_name <template name> --template_parameters <template parameters json>
125-
```
126-
127-
Run the sample like this to provision using Csr:
128-
129-
``` sh
130-
./fleet-provisioning --endpoint <endpoint> --ca_file <path to root CA>
131-
--cert <path to the certificate> --key <path to the private key>
132-
--template_name <template name> --template_parameters <template parameters json> --csr <path to the CSR in PEM format>
133-
```
134-
135-
Your Thing's
136-
[Policy](https://docs.aws.amazon.com/iot/latest/developerguide/iot-policies.html)
137-
must provide privileges for this sample to connect, subscribe, publish,
138-
and receive.
139-
140-
<details>
141-
<summary>(see sample policy)</summary>
142-
<pre>
143-
{
144-
"Version": "2012-10-17",
145-
"Statement": [
146-
{
147-
"Effect": "Allow",
148-
"Action": [
149-
"iot:Publish"
150-
],
151-
"Resource": [
152-
"arn:aws:iot:<b>region</b>:<b>account</b>:topic/$aws/certificates/create/json",
153-
"arn:aws:iot:<b>region</b>:<b>account</b>:topic/$aws/certificates/create-from-csr/json",
154-
"arn:aws:iot:<b>region</b>:<b>account</b>:topic/$aws/provisioning-templates/<b>templatename</b>/provision/json"
155-
]
156-
},
157-
{
158-
"Effect": "Allow",
159-
"Action": [
160-
"iot:Receive",
161-
"iot:Subscribe"
162-
],
163-
"Resource": [
164-
"arn:aws:iot:<b>region</b>:<b>account</b>:topic/$aws/certificates/create/json/accepted",
165-
"arn:aws:iot:<b>region</b>:<b>account</b>:topic/$aws/certificates/create/json/rejected",
166-
"arn:aws:iot:<b>region</b>:<b>account</b>:topic/$aws/certificates/create-from-csr/json/accepted",
167-
"arn:aws:iot:<b>region</b>:<b>account</b>:topic/$aws/certificates/create-from-csr/json/rejected",
168-
"arn:aws:iot:<b>region</b>:<b>account</b>:topic/$aws/provisioning-templates/<b>templatename</b>/provision/json/accepted",
169-
"arn:aws:iot:<b>region</b>:<b>account</b>:topic/$aws/provisioning-templates/<b>templatename</b>/provision/json/rejected"
170-
]
171-
},
172-
{
173-
"Effect": "Allow",
174-
"Action": "iot:Connect",
175-
"Resource": "arn:aws:iot:<b>region</b>:<b>account</b>:client/test-*"
176-
}
177-
]
178-
}
179-
</pre>
180-
</details>
105+
source: `samples/mqtt/raw_pub_sub/main.cpp`
181106

182107
## Shadow
183108

@@ -199,7 +124,7 @@ value. When the sample learns of a new desired value, that value is changed
199124
on the device and an update is sent to the server with the new "reported"
200125
value.
201126

202-
Source: `samples/shadow/shadow_sync`
127+
Source: `samples/shadow/shadow_sync/main.cpp`
203128

204129
Your Thing's
205130
[Policy](https://docs.aws.amazon.com/iot/latest/developerguide/iot-policies.html)
@@ -271,7 +196,7 @@ This sample requires you to create jobs for your device to execute. See
271196

272197
On startup, the sample describes a job that is pending execution.
273198

274-
Source: `samples/jobs/describe_job_execution`
199+
Source: `samples/jobs/describe_job_execution/main.cpp`
275200

276201
Your Thing's
277202
[Policy](https://docs.aws.amazon.com/iot/latest/developerguide/iot-policies.html)
@@ -331,6 +256,193 @@ and receive.
331256
</pre>
332257
</details>
333258

259+
## Fleet provisioning
260+
261+
This sample uses the AWS IoT
262+
[Fleet provisioning](https://docs.aws.amazon.com/iot/latest/developerguide/provision-wo-cert.html)
263+
to provision devices using either a CSR or KeysAndcertificate and subsequently calls RegisterThing.
264+
265+
On startup, the script subscribes to topics based on the request type of either CSR or Keys topics,
266+
publishes the request to corresponding topic and calls RegisterThing.
267+
268+
Source: `samples/identity/fleet_provisioning/main.cpp`
269+
270+
Run the sample like this to provision using CreateKeysAndCertificate:
271+
272+
``` sh
273+
./fleet-provisioning --endpoint <endpoint> --ca_file <path to root CA>
274+
--cert <path to the certificate> --key <path to the private key>
275+
--template_name <template name> --template_parameters <template parameters json>
276+
```
277+
278+
Run the sample like this to provision using Csr:
279+
280+
``` sh
281+
./fleet-provisioning --endpoint <endpoint> --ca_file <path to root CA>
282+
--cert <path to the certificate> --key <path to the private key>
283+
--template_name <template name> --template_parameters <template parameters json> --csr <path to the CSR in PEM format>
284+
```
285+
286+
Your Thing's
287+
[Policy](https://docs.aws.amazon.com/iot/latest/developerguide/iot-policies.html)
288+
must provide privileges for this sample to connect, subscribe, publish,
289+
and receive.
290+
291+
<details>
292+
<summary>(see sample policy)</summary>
293+
<pre>
294+
{
295+
"Version": "2012-10-17",
296+
"Statement": [
297+
{
298+
"Effect": "Allow",
299+
"Action": [
300+
"iot:Publish"
301+
],
302+
"Resource": [
303+
"arn:aws:iot:<b>region</b>:<b>account</b>:topic/$aws/certificates/create/json",
304+
"arn:aws:iot:<b>region</b>:<b>account</b>:topic/$aws/certificates/create-from-csr/json",
305+
"arn:aws:iot:<b>region</b>:<b>account</b>:topic/$aws/provisioning-templates/<b>templatename</b>/provision/json"
306+
]
307+
},
308+
{
309+
"Effect": "Allow",
310+
"Action": [
311+
"iot:Receive",
312+
"iot:Subscribe"
313+
],
314+
"Resource": [
315+
"arn:aws:iot:<b>region</b>:<b>account</b>:topic/$aws/certificates/create/json/accepted",
316+
"arn:aws:iot:<b>region</b>:<b>account</b>:topic/$aws/certificates/create/json/rejected",
317+
"arn:aws:iot:<b>region</b>:<b>account</b>:topic/$aws/certificates/create-from-csr/json/accepted",
318+
"arn:aws:iot:<b>region</b>:<b>account</b>:topic/$aws/certificates/create-from-csr/json/rejected",
319+
"arn:aws:iot:<b>region</b>:<b>account</b>:topic/$aws/provisioning-templates/<b>templatename</b>/provision/json/accepted",
320+
"arn:aws:iot:<b>region</b>:<b>account</b>:topic/$aws/provisioning-templates/<b>templatename</b>/provision/json/rejected"
321+
]
322+
},
323+
{
324+
"Effect": "Allow",
325+
"Action": "iot:Connect",
326+
"Resource": "arn:aws:iot:<b>region</b>:<b>account</b>:client/test-*"
327+
}
328+
]
329+
}
330+
</pre>
331+
</details>
332+
333+
### Fleet Provisioning Detailed Instructions
334+
335+
#### Aws Resource Setup
336+
337+
Fleet provisioning requires some additional AWS resources be set up first. This section documents the steps you need to take to
338+
get the sample up and running. These steps assume you have the AWS CLI installed and the default user/credentials has
339+
sufficient permission to perform all of the listed operations. You will also need python3 to be able to run parse_cert_set_result.py. These steps are based on provisioning setup steps
340+
that can be found at [Embedded C SDK Setup](https://docs.aws.amazon.com/freertos/latest/lib-ref/c-sdk/provisioning/provisioning_tests.html#provisioning_system_tests_setup).
341+
342+
First, create the IAM role that will be needed by the fleet provisioning template. Replace `RoleName` with a name of the role you want to create.
343+
``` sh
344+
aws iam create-role \
345+
--role-name [RoleName] \
346+
--assume-role-policy-document '{"Version":"2012-10-17","Statement":[{"Action":"sts:AssumeRole","Effect":"Allow","Principal":{"Service":"iot.amazonaws.com"}}]}'
347+
```
348+
Next, attach a policy to the role created in the first step. Replace `RoleName` with the name of the role you created previously.
349+
``` sh
350+
aws iam attach-role-policy \
351+
--role-name [RoleName] \
352+
--policy-arn arn:aws:iam::aws:policy/service-role/AWSIoTThingsRegistration
353+
```
354+
Finally, create the template resource which will be used for provisioning by the demo application. This needs to be done only
355+
once. To create a template, the following AWS CLI command may be used. Replace `TemplateName` with the name of the fleet
356+
provisioning template you want to create. Replace `RoleName` with the name of the role you created previously. Replace
357+
`TemplateJSON` with the template body as a JSON string (containing escape characters). Replace `account` with your AWS
358+
account number.
359+
``` sh
360+
aws iot create-provisioning-template \
361+
--template-name [TemplateName] \
362+
--provisioning-role-arn arn:aws:iam::[account]:role/[RoleName] \
363+
--template-body "[TemplateJSON]" \
364+
--enabled
365+
```
366+
The rest of the instructions assume you have used the following for the template body:
367+
``` sh
368+
{\"Parameters\":{\"DeviceLocation\":{\"Type\":\"String\"},\"AWS::IoT::Certificate::Id\":{\"Type\":\"String\"},\"SerialNumber\":{\"Type\":\"String\"}},\"Mappings\":{\"LocationTable\":{\"Seattle\":{\"LocationUrl\":\"https://example.aws\"}}},\"Resources\":{\"thing\":{\"Type\":\"AWS::IoT::Thing\",\"Properties\":{\"ThingName\":{\"Fn::Join\":[\"\",[\"ThingPrefix_\",{\"Ref\":\"SerialNumber\"}]]},\"AttributePayload\":{\"version\":\"v1\",\"serialNumber\":\"serialNumber\"}},\"OverrideSettings\":{\"AttributePayload\":\"MERGE\",\"ThingTypeName\":\"REPLACE\",\"ThingGroups\":\"DO_NOTHING\"}},\"certificate\":{\"Type\":\"AWS::IoT::Certificate\",\"Properties\":{\"CertificateId\":{\"Ref\":\"AWS::IoT::Certificate::Id\"},\"Status\":\"Active\"},\"OverrideSettings\":{\"Status\":\"REPLACE\"}},\"policy\":{\"Type\":\"AWS::IoT::Policy\",\"Properties\":{\"PolicyDocument\":{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":[\"iot:Connect\",\"iot:Subscribe\",\"iot:Publish\",\"iot:Receive\"],\"Resource\":\"*\"}]}}}},\"DeviceConfiguration\":{\"FallbackUrl\":\"https://www.example.com/test-site\",\"LocationUrl\":{\"Fn::FindInMap\":[\"LocationTable\",{\"Ref\":\"DeviceLocation\"},\"LocationUrl\"]}}}
369+
```
370+
If you use a different body, you may need to pass in different template parameters.
371+
372+
#### Running the sample and provisioning using a certificate-key set from a provisioning claim
373+
374+
To run the provisioning sample, you'll need a certificate and key set with sufficient permissions. Provisioning certificates are normally
375+
created ahead of time and placed on your device, but for this sample, we will just create them on the fly. You can also
376+
use any certificate set you've already created if it has sufficient IoT permissions and in doing so, you can skip the step
377+
that calls `create-provisioning-claim`.
378+
379+
We've included a script in the utils folder that creates certificate and key files from the response of calling
380+
`create-provisioning-claim`. These dynamically sourced certificates are only valid for five minutes. When running the command,
381+
you'll need to substitute the name of the template you previously created, and on Windows, replace the paths with something appropriate.
382+
383+
(Optional) Create a temporary provisioning claim certificate set. This command is executed in the debug folder(`aws-iot-device-sdk-cpp-v2-build\samples\identity\fleet_provisioning\Debug`):
384+
``` sh
385+
aws iot create-provisioning-claim \
386+
--template-name [TemplateName] \
387+
| python3 ../../../../../aws-iot-device-sdk-cpp-v2/utils/parse_cert_set_result.py \
388+
--path /tmp \
389+
--filename provision
390+
```
391+
392+
The provisioning claim's cert and key set have been written to `/tmp/provision*`. Now you can use these temporary keys
393+
to perform the actual provisioning. If you are not using the temporary provisioning certificate, replace the paths for `--cert`
394+
and `--key` appropriately:
395+
396+
``` sh
397+
./fleet-provisioning \
398+
--endpoint [your endpoint]-ats.iot.[region].amazonaws.com \
399+
--ca_file [pathToRootCA] \
400+
--cert /tmp/provision.cert.pem \
401+
--key /tmp/provision.private.key \
402+
--template_name [TemplateName] \
403+
--template_parameters "{\"SerialNumber\":\"1\",\"DeviceLocation\":\"Seattle\"}"
404+
```
405+
406+
Notice that we provided substitution values for the two parameters in the template body, `DeviceLocation` and `SerialNumber`.
407+
408+
#### Run the sample using the certificate signing request workflow
409+
410+
To run the sample with this workflow, you'll need to create a certificate signing request.
411+
412+
First create a certificate-key pair:
413+
``` sh
414+
openssl genrsa -out /tmp/deviceCert.key 2048
415+
```
416+
417+
Next create a certificate signing request from it:
418+
``` sh
419+
openssl req -new -key /tmp/deviceCert.key -out /tmp/deviceCert.csr
420+
```
421+
422+
(Optional) As with the previous workflow, we'll create a temporary certificate set from a provisioning claim. This step can
423+
be skipped if you're using a certificate set capable of provisioning the device. This command is executed in the debug folder(`aws-iot-device-sdk-cpp-v2-build\samples\identity\fleet_provisioning\Debug`):
424+
425+
``` sh
426+
aws iot create-provisioning-claim \
427+
--template-name [TemplateName] \
428+
| python3 ../../../../../aws-iot-device-sdk-cpp-v2/utils/parse_cert_set_result.py \
429+
--path /tmp \
430+
--filename provision
431+
```
432+
433+
Finally, supply the certificate signing request while invoking the provisioning sample. As with the previous workflow, if
434+
using a permanent certificate set, replace the paths specified in the `--cert` and `--key` arguments:
435+
``` sh
436+
./fleet-provisioning \
437+
--endpoint [your endpoint]-ats.iot.[region].amazonaws.com \
438+
--ca_file [pathToRootCA] \
439+
--cert /tmp/provision.cert.pem \
440+
--key /tmp/provision.private.key \
441+
--template_name [TemplateName] \
442+
--template_parameters "{\"SerialNumber\":\"1\",\"DeviceLocation\":\"Seattle\"}" \
443+
--csr /tmp/deviceCert.csr
444+
```
445+
334446
## Secure Tunneling
335447

336448
This sample uses the AWS IoT [Secure Tunneling](https://docs.aws.amazon.com/iot/latest/developerguide/secure-tunneling.html) Service to receive a tunnel notification.

utils/parse_cert_set_result.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0.
3+
4+
"""
5+
6+
A simply utility script to parse out certificates and keys from some IoT operations.
7+
8+
For example, you may be doing fleet provisioning and want to have a simple way of setting up the results from create-provisioning-claim
9+
into usable pem files that you can make IoT connections with.
10+
11+
Example usage:
12+
13+
aws iot create-provisioning-claim --template-name <TemplateName> | python3 parse_cert_set_result.py --path <PathToOutputtedCerts> --filename <Filename>
14+
15+
"""
16+
17+
import argparse
18+
import json
19+
import os
20+
import re
21+
import sys
22+
23+
parser = argparse.ArgumentParser(description="Utility script to generate valid .cert.pem, .private.key, .public.key files from the JSON response of CreateProvisioningClaim, CreateCertificateFromCsr")
24+
parser.add_argument('--path', required=True, help="Path to extract the certificate set files to. Created if does not exist")
25+
parser.add_argument('--filename', required=True, help="Filename (prefix) to use for the generated files")
26+
27+
if __name__ == '__main__':
28+
# Process input args
29+
args = parser.parse_args()
30+
31+
path = args.path
32+
filename = args.filename
33+
34+
if not os.path.exists(path):
35+
os.makedirs(path)
36+
37+
body = json.load(sys.stdin)
38+
39+
raw_pem = body['certificatePem']
40+
if raw_pem:
41+
pem = re.sub("\\n", "\n", raw_pem)
42+
pem_filename = os.path.join(path, filename + ".cert.pem")
43+
with open(pem_filename, 'w') as file:
44+
file.write(pem)
45+
46+
try:
47+
raw_pub_key = body['keyPair']['PublicKey']
48+
if raw_pub_key:
49+
pub_key = re.sub("\\n", "\n", raw_pub_key)
50+
pub_key_filename = os.path.join(path, filename + ".public.key")
51+
with open(pub_key_filename, 'w') as file:
52+
file.write(pub_key)
53+
54+
raw_private_key = body['keyPair']['PrivateKey']
55+
if raw_private_key:
56+
private_key = re.sub("\\n", "\n", raw_private_key)
57+
private_key_filename = os.path.join(path, filename + ".private.key")
58+
with open(private_key_filename, 'w') as file:
59+
file.write(private_key)
60+
except KeyError:
61+
pass
62+
63+
print("Success!")
64+
65+
66+
67+
68+
69+

0 commit comments

Comments
 (0)