From 972c8c67d76675eadc1f810037a406e292c2f525 Mon Sep 17 00:00:00 2001 From: Noah Beard Date: Wed, 15 Mar 2023 17:05:08 -0400 Subject: [PATCH 01/11] Add X509 sample --- .github/workflows/ci.yml | 9 +++ .../workflows/ci_run_x509_connect_cfg.json | 43 ++++++++++++ samples/utils/command_line_utils.py | 67 +++++++++++++++++++ samples/x509_connect.md | 52 ++++++++++++++ samples/x509_connect.py | 58 ++++++++++++++++ 5 files changed, 229 insertions(+) create mode 100644 .github/workflows/ci_run_x509_connect_cfg.json create mode 100644 samples/x509_connect.md create mode 100644 samples/x509_connect.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8de9f99e..af8de674 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,6 +24,7 @@ env: CI_IOT_CONTAINERS_ROLE: ${{ secrets.AWS_CI_IOT_CONTAINERS }} CI_PUBSUB_ROLE: ${{ secrets.AWS_CI_PUBSUB_ROLE }} CI_COGNITO_ROLE: ${{ secrets.AWS_CI_COGNITO_ROLE }} + CI_X509_ROLE: $${{ secrets.AWS_CI_X509_ROLE }} CI_CUSTOM_AUTHORIZER_ROLE: ${{ secrets.AWS_CI_CUSTOM_AUTHORIZER_ROLE }} CI_SHADOW_ROLE: ${{ secrets.AWS_CI_SHADOW_ROLE }} CI_JOBS_ROLE: ${{ secrets.AWS_CI_JOBS_ROLE }} @@ -253,6 +254,14 @@ jobs: - name: run Cognito Connect sample run: | python3 ${{ env.CI_UTILS_FOLDER }}/run_sample_ci.py --file ${{ env.CI_SAMPLES_CFG_FOLDER }}/ci_run_cognito_connect_cfg.json + - name: configure AWS credentials (X509) + uses: aws-actions/configure-aws-credentials@v1 + with: + role-to-assume: ${{ env.CI_X509_ROLE }} + aws-region: ${{ env.AWS_DEFAULT_REGION }} + - name: run X509 sample + run: | + python3 ${{ env.CI_UTILS_FOLDER }}/run_sample_ci.py --file ${{ env.CI_SAMPLES_CFG_FOLDER }}/ci_run_x509_connect_cfg.json - name: configure AWS credentials (MQTT5 samples) uses: aws-actions/configure-aws-credentials@v1 with: diff --git a/.github/workflows/ci_run_x509_connect_cfg.json b/.github/workflows/ci_run_x509_connect_cfg.json new file mode 100644 index 00000000..e2292ef1 --- /dev/null +++ b/.github/workflows/ci_run_x509_connect_cfg.json @@ -0,0 +1,43 @@ +{ + "language": "Python", + "sample_file": "./aws-iot-device-sdk-python-v2/samples/x509_connect.py", + "sample_region": "us-east-1", + "sample_main_class": "", + "arguments": [ + { + "name": "--endpoint", + "secret": "ci/endpoint" + }, + { + "name": "--x509_cert", + "secret": "ci/PubSub/cert", + "filename": "tmp_certificate.pem" + }, + { + "name": "--x509_key", + "secret": "ci/PubSub/key", + "filename": "tmp_key.pem" + }, + { + "name": "--x509_ca_file", + "secret": "ci/PubSub/ca", + "filename": "tmp_ca.pem" + }, + { + "name": "--x509_endpoint", + "secret": "ci/X509/endpoint_credentials" + }, + { + "name": "--x509_role_alias", + "secret": "ci/X509/alias" + }, + { + "name": "--signing_region", + "data": "us-east-1" + }, + { + "name": "--x509_thing_name", + "data": "CI_PubSub_Thing" + } + ] +} diff --git a/samples/utils/command_line_utils.py b/samples/utils/command_line_utils.py index b622bdd8..9489d70a 100644 --- a/samples/utils/command_line_utils.py +++ b/samples/utils/command_line_utils.py @@ -143,6 +143,39 @@ def add_common_custom_authorizer_commands(self): "", "The password to send when connecting through a custom authorizer (optional)") + def add_common_x509_commands(self): + self.register_command( + self.m_cmd_x509_endpoint, + "", + "The credentials endpoint to fetch x509 credentials from", + ) + self.register_command( + self.m_cmd_x509_thing_name, + "", + "Thing name to fetch x509 credentials on behalf of" + ) + self.register_command( + self.m_cmd_x509_role_alias, + "", + "Role alias to use with the x509 credentials provider" + ) + self.register_command( + self.m_cmd_x509_key, + "", + "Path to the IoT thing private key used in fetching x509 credentials" + ) + self.register_command( + self.m_cmd_x509_cert, + "", + "Path to the IoT thing certificate used in fetching x509 credentials" + ) + + self.register_command( + self.m_cmd_x509_ca, + "", + "TODO" + ) + """ Returns the command if it exists and has been passed to the console, otherwise it will print the help for the sample and exit the application. """ @@ -211,6 +244,34 @@ def build_websocket_mqtt_connection(self, on_connection_interrupted, on_connecti keep_alive_secs=30) return mqtt_connection + def build_websocket_x509_mqtt_connection(self, on_connection_interrupted, on_connection_resumed): + proxy_options = self.get_proxy_options_for_mqtt_connection() + + x509_tls_options = io.TlsContextOptions.create_client_with_mtls_from_path( + self.get_command_required(self.m_cmd_x509_cert), + self.get_command_required(self.m_cmd_x509_key)) + x509_tls_options.ca_dirpath = self.get_command(self.m_cmd_x509_ca, None) + + x509_provider = auth.AwsCredentialsProvider.new_x509( + endpoint=self.get_command_required(self.m_cmd_x509_endpoint), + thing_name=self.get_command_required(self.m_cmd_x509_thing_name), + role_alias=self.get_command_required(self.m_cmd_x509_role_alias), + tls_ctx=io.ClientTlsContext(x509_tls_options), + http_proxy_options=proxy_options + ) + mqtt_connection = mqtt_connection_builder.websockets_with_default_aws_signing( + endpoint=self.get_command_required(self.m_cmd_endpoint), + region=self.get_command_required(self.m_cmd_signing_region), + credentials_provider=x509_provider, + http_proxy_options=proxy_options, + ca_filepath=self.get_command(self.m_cmd_ca_file), + on_connection_interrupted=on_connection_interrupted, + on_connection_resumed=on_connection_resumed, + client_id=self.get_command_required("client_id"), + clean_session=False, + keep_alive_secs=30) + return mqtt_connection + def build_cognito_mqtt_connection(self, on_connection_interrupted, on_connection_resumed): proxy_options = self.get_proxy_options_for_mqtt_connection() @@ -400,3 +461,9 @@ def build_mqtt5_client(self, m_cmd_custom_auth_authorizer_signature = "custom_auth_authorizer_signature" m_cmd_custom_auth_password = "custom_auth_password" m_cmd_cognito_identity = "cognito_identity" + m_cmd_x509_endpoint = "x509_endpoint" + m_cmd_x509_thing_name = "x509_thing_name" + m_cmd_x509_role_alias = "x509_role_alias" + m_cmd_x509_cert = "x509_cert" + m_cmd_x509_key = "x509_key" + m_cmd_x509_ca = "x509_ca" diff --git a/samples/x509_connect.md b/samples/x509_connect.md new file mode 100644 index 00000000..53fa1754 --- /dev/null +++ b/samples/x509_connect.md @@ -0,0 +1,52 @@ +# x509 Credentials Provider Connect + +[**Return to main sample list**](./README.md) + +This sample is similar to the [Basic Connect](./basic_connect.md), but the connection uses a X.509 certificate +to source the AWS credentials when connecting. + +See the [Authorizing direct calls to AWS services using AWS IoT Core credential provider](https://docs.aws.amazon.com/iot/latest/developerguide/authorizing-direct-aws.html) page for instructions on how to setup the IAM roles, the trust policy for the IAM roles, how to setup the IoT Core Role alias, and how to get the credential provider endpoint for your AWS account. + +Your IoT Core Thing's [Policy](https://docs.aws.amazon.com/iot/latest/developerguide/iot-policies.html) must provide privileges for this sample to connect. Below is a sample policy that can be used on your IoT Core Thing that will allow this sample to run as intended. + +
+(see sample policy) +
+{
+  "Version": "2012-10-17",
+  "Statement": [
+    {
+      "Effect": "Allow",
+      "Action": [
+        "iot:Connect"
+      ],
+      "Resource": [
+        "arn:aws:iot:region:account:client/test-*"
+      ]
+    },
+    {
+      "Effect":"Allow",
+      "Action":"iot:AssumeRoleWithCertificate",
+      "Resource":"arn:aws:iot:region:account:rolealias/role-alias"
+    }
+  ]
+}
+
+ +Replace with the following with the data from your AWS account: +* ``: The AWS IoT Core region where you created your AWS IoT Core thing you wish to use with this sample. For example `us-east-1`. +* ``: Your AWS IoT Core account ID. This is the set of numbers in the top right next to your AWS account name when using the AWS IoT Core website. +* ``: The X509 role alias you created and wish to connect using. + +Note that in a real application, you may want to avoid the use of wildcards in your ClientID or use them selectively. Please follow best practices when working with AWS on production applications using the SDK. Also, for the purposes of this sample, please make sure your policy allows a client ID of `test-*` to connect or use `--client_id ` to send the client ID your policy supports. + +
+ +## How to run + +To run the x509 Credentials Provider Connect sample use the following command: + +``` sh +# For Windows: replace 'python3' with 'python' and '/' with '\' +python3 x509_connect.py --endpoint --signing_region --x509_cert --x509_endpoint --x509_key --x509_role_alias -x509_thing_name +``` diff --git a/samples/x509_connect.py b/samples/x509_connect.py new file mode 100644 index 00000000..fd359a7f --- /dev/null +++ b/samples/x509_connect.py @@ -0,0 +1,58 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0. + +from uuid import uuid4 + +# This sample shows how to create a MQTT connection using X509 files to connect. +# This sample is intended to be used as a reference for making MQTT connections via X509. + +# Parse arguments +import utils.command_line_utils as command_line_utils +cmdUtils = command_line_utils.CommandLineUtils("Basic Connect - Make a MQTT connection.") +cmdUtils.add_common_mqtt_commands() +cmdUtils.add_common_proxy_commands() +cmdUtils.add_common_logging_commands() +cmdUtils.add_common_x509_commands() +cmdUtils.register_command("signing_region", "", + "The signing region used for the websocket signer", + True, str) +cmdUtils.register_command("client_id", "", + "Client ID to use for MQTT connection (optional, default='test-*').", + default="test-" + str(uuid4())) +cmdUtils.register_command("is_ci", "", "If present the sample will run in CI mode (optional, default='None')") +# Needs to be called so the command utils parse the commands +cmdUtils.get_args() +is_ci = cmdUtils.get_command("is_ci", None) is not None + +# Callback when connection is accidentally lost. +def on_connection_interrupted(connection, error, **kwargs): + print("Connection interrupted. error: {}".format(error)) + +# Callback when an interrupted connection is re-established. +def on_connection_resumed(connection, return_code, session_present, **kwargs): + print("Connection resumed. return_code: {} session_present: {}".format(return_code, session_present)) + + +if __name__ == '__main__': + # Create a connection using X509 authentication to connect + # Note: The data for the connection is gotten from cmdUtils. + # (see build_websocket_x509_mqtt_connection for implementation) + mqtt_connection = cmdUtils.build_websocket_x509_mqtt_connection(on_connection_interrupted, on_connection_resumed) + + if not is_ci: + print("Connecting to {} with client ID '{}'...".format( + cmdUtils.get_command(cmdUtils.m_cmd_endpoint), cmdUtils.get_command("client_id"))) + else: + print("Connecting to endpoint with client ID") + + connect_future = mqtt_connection.connect() + + # Future.result() waits until a result is available + connect_future.result() + print("Connected!") + + # Disconnect + print("Disconnecting...") + disconnect_future = mqtt_connection.disconnect() + disconnect_future.result() + print("Disconnected!") From 1722e9b4186e569f31c18f34632463758b0ec49d Mon Sep 17 00:00:00 2001 From: Noah Beard Date: Thu, 16 Mar 2023 09:10:01 -0400 Subject: [PATCH 02/11] Use latest CRT release --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 78279ecb..8e6cfd06 100644 --- a/setup.py +++ b/setup.py @@ -40,7 +40,7 @@ def _load_version(): "Operating System :: OS Independent", ], install_requires=[ - 'awscrt==0.16.10', + 'awscrt==0.16.13', ], python_requires='>=3.7', ) From 67a05bc9f103eefea7f11af50aa7d5a40abd57e0 Mon Sep 17 00:00:00 2001 From: Noah Beard Date: Thu, 16 Mar 2023 09:15:58 -0400 Subject: [PATCH 03/11] Fix silly typo in CI file causing it to fail --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index af8de674..7c07625c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ env: CI_IOT_CONTAINERS_ROLE: ${{ secrets.AWS_CI_IOT_CONTAINERS }} CI_PUBSUB_ROLE: ${{ secrets.AWS_CI_PUBSUB_ROLE }} CI_COGNITO_ROLE: ${{ secrets.AWS_CI_COGNITO_ROLE }} - CI_X509_ROLE: $${{ secrets.AWS_CI_X509_ROLE }} + CI_X509_ROLE: ${{ secrets.AWS_CI_X509_ROLE }} CI_CUSTOM_AUTHORIZER_ROLE: ${{ secrets.AWS_CI_CUSTOM_AUTHORIZER_ROLE }} CI_SHADOW_ROLE: ${{ secrets.AWS_CI_SHADOW_ROLE }} CI_JOBS_ROLE: ${{ secrets.AWS_CI_JOBS_ROLE }} From e7ba2b7aeb24ec3fc55f77382bc94bb9408e7ceb Mon Sep 17 00:00:00 2001 From: Noah Beard Date: Thu, 16 Mar 2023 09:21:27 -0400 Subject: [PATCH 04/11] Make x509 CA file consistent, add docs for it --- samples/utils/command_line_utils.py | 4 ++-- samples/x509_connect.md | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/samples/utils/command_line_utils.py b/samples/utils/command_line_utils.py index 9489d70a..2cd3308c 100644 --- a/samples/utils/command_line_utils.py +++ b/samples/utils/command_line_utils.py @@ -173,7 +173,7 @@ def add_common_x509_commands(self): self.register_command( self.m_cmd_x509_ca, "", - "TODO" + "Path to the root certificate used in fetching x509 credentials" ) """ @@ -466,4 +466,4 @@ def build_mqtt5_client(self, m_cmd_x509_role_alias = "x509_role_alias" m_cmd_x509_cert = "x509_cert" m_cmd_x509_key = "x509_key" - m_cmd_x509_ca = "x509_ca" + m_cmd_x509_ca = "x509_ca_file" diff --git a/samples/x509_connect.md b/samples/x509_connect.md index 53fa1754..c1a0a5c2 100644 --- a/samples/x509_connect.md +++ b/samples/x509_connect.md @@ -50,3 +50,10 @@ To run the x509 Credentials Provider Connect sample use the following command: # For Windows: replace 'python3' with 'python' and '/' with '\' python3 x509_connect.py --endpoint --signing_region --x509_cert --x509_endpoint --x509_key --x509_role_alias -x509_thing_name ``` + +You can also pass a Certificate Authority file (CA) if your X509 certificate and key combination requires it: + +``` sh +# For Windows: replace 'python3' with 'python' and '/' with '\' +python3 x509_connect.py --endpoint --signing_region --x509_cert --x509_endpoint --x509_key --x509_role_alias -x509_thing_name --x509_ca_file +``` From ab772cae7c55fd053ca1e4fd770a7fd3251a2cc9 Mon Sep 17 00:00:00 2001 From: Noah Beard Date: Thu, 16 Mar 2023 10:19:12 -0400 Subject: [PATCH 05/11] Keep the TLS separate? --- samples/utils/command_line_utils.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/samples/utils/command_line_utils.py b/samples/utils/command_line_utils.py index 2cd3308c..b2ece584 100644 --- a/samples/utils/command_line_utils.py +++ b/samples/utils/command_line_utils.py @@ -250,13 +250,14 @@ def build_websocket_x509_mqtt_connection(self, on_connection_interrupted, on_con x509_tls_options = io.TlsContextOptions.create_client_with_mtls_from_path( self.get_command_required(self.m_cmd_x509_cert), self.get_command_required(self.m_cmd_x509_key)) - x509_tls_options.ca_dirpath = self.get_command(self.m_cmd_x509_ca, None) + x509_tls_options.ca_dirpath = self.get_command(self.m_cmd_x509_ca) + x509_tls_context = io.ClientTlsContext(x509_tls_options) x509_provider = auth.AwsCredentialsProvider.new_x509( endpoint=self.get_command_required(self.m_cmd_x509_endpoint), thing_name=self.get_command_required(self.m_cmd_x509_thing_name), role_alias=self.get_command_required(self.m_cmd_x509_role_alias), - tls_ctx=io.ClientTlsContext(x509_tls_options), + tls_ctx=x509_tls_context, http_proxy_options=proxy_options ) mqtt_connection = mqtt_connection_builder.websockets_with_default_aws_signing( From cbc0ee8e0a3dd81c53c5d1ff1ff8f38ff074278d Mon Sep 17 00:00:00 2001 From: Noah Beard Date: Thu, 16 Mar 2023 11:06:33 -0400 Subject: [PATCH 06/11] Enable error logging and print the exception to hopefully figure out what is going on --- samples/x509_connect.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/samples/x509_connect.py b/samples/x509_connect.py index fd359a7f..ccb9e7e9 100644 --- a/samples/x509_connect.py +++ b/samples/x509_connect.py @@ -34,6 +34,9 @@ def on_connection_resumed(connection, return_code, session_present, **kwargs): if __name__ == '__main__': + # TMP - enable logging + io.init_logging(io.LogLevel.Error, 'stderr') + # Create a connection using X509 authentication to connect # Note: The data for the connection is gotten from cmdUtils. # (see build_websocket_x509_mqtt_connection for implementation) @@ -48,7 +51,11 @@ def on_connection_resumed(connection, return_code, session_present, **kwargs): connect_future = mqtt_connection.connect() # Future.result() waits until a result is available - connect_future.result() + try: + connect_future.result() + except Exception as e: + print (e) + raise RuntimeError(e) print("Connected!") # Disconnect From 8d95ce26bf0bd7b3caa9d675f6eae2d1f2be977d Mon Sep 17 00:00:00 2001 From: Noah Beard Date: Thu, 16 Mar 2023 11:10:05 -0400 Subject: [PATCH 07/11] Add missing import --- samples/x509_connect.py | 1 + 1 file changed, 1 insertion(+) diff --git a/samples/x509_connect.py b/samples/x509_connect.py index ccb9e7e9..3bf4866f 100644 --- a/samples/x509_connect.py +++ b/samples/x509_connect.py @@ -1,6 +1,7 @@ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0. +from awscrt import io from uuid import uuid4 # This sample shows how to create a MQTT connection using X509 files to connect. From 963f28ec79907186ea72ab3683450c10bf68fe5c Mon Sep 17 00:00:00 2001 From: Noah Beard Date: Thu, 16 Mar 2023 11:19:49 -0400 Subject: [PATCH 08/11] Try avoiding the CA file? --- samples/utils/command_line_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/utils/command_line_utils.py b/samples/utils/command_line_utils.py index b2ece584..5037a237 100644 --- a/samples/utils/command_line_utils.py +++ b/samples/utils/command_line_utils.py @@ -250,7 +250,7 @@ def build_websocket_x509_mqtt_connection(self, on_connection_interrupted, on_con x509_tls_options = io.TlsContextOptions.create_client_with_mtls_from_path( self.get_command_required(self.m_cmd_x509_cert), self.get_command_required(self.m_cmd_x509_key)) - x509_tls_options.ca_dirpath = self.get_command(self.m_cmd_x509_ca) + # x509_tls_options.ca_dirpath = self.get_command(self.m_cmd_x509_ca) x509_tls_context = io.ClientTlsContext(x509_tls_options) x509_provider = auth.AwsCredentialsProvider.new_x509( From d04523d915eba90add14ecff04fb7907b137d07b Mon Sep 17 00:00:00 2001 From: Noah Beard Date: Thu, 16 Mar 2023 11:23:16 -0400 Subject: [PATCH 09/11] CA file seems to be issue. Ignore it for this sample --- .github/workflows/ci_run_x509_connect_cfg.json | 5 ----- samples/utils/command_line_utils.py | 3 ++- samples/x509_connect.py | 9 +-------- 3 files changed, 3 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci_run_x509_connect_cfg.json b/.github/workflows/ci_run_x509_connect_cfg.json index e2292ef1..ac64d384 100644 --- a/.github/workflows/ci_run_x509_connect_cfg.json +++ b/.github/workflows/ci_run_x509_connect_cfg.json @@ -18,11 +18,6 @@ "secret": "ci/PubSub/key", "filename": "tmp_key.pem" }, - { - "name": "--x509_ca_file", - "secret": "ci/PubSub/ca", - "filename": "tmp_ca.pem" - }, { "name": "--x509_endpoint", "secret": "ci/X509/endpoint_credentials" diff --git a/samples/utils/command_line_utils.py b/samples/utils/command_line_utils.py index 5037a237..6679b70d 100644 --- a/samples/utils/command_line_utils.py +++ b/samples/utils/command_line_utils.py @@ -250,7 +250,8 @@ def build_websocket_x509_mqtt_connection(self, on_connection_interrupted, on_con x509_tls_options = io.TlsContextOptions.create_client_with_mtls_from_path( self.get_command_required(self.m_cmd_x509_cert), self.get_command_required(self.m_cmd_x509_key)) - # x509_tls_options.ca_dirpath = self.get_command(self.m_cmd_x509_ca) + if (self.get_command(self.m_cmd_x509_ca) != None): + x509_tls_options.ca_dirpath = self.get_command(self.m_cmd_x509_ca) x509_tls_context = io.ClientTlsContext(x509_tls_options) x509_provider = auth.AwsCredentialsProvider.new_x509( diff --git a/samples/x509_connect.py b/samples/x509_connect.py index 3bf4866f..535ef953 100644 --- a/samples/x509_connect.py +++ b/samples/x509_connect.py @@ -35,9 +35,6 @@ def on_connection_resumed(connection, return_code, session_present, **kwargs): if __name__ == '__main__': - # TMP - enable logging - io.init_logging(io.LogLevel.Error, 'stderr') - # Create a connection using X509 authentication to connect # Note: The data for the connection is gotten from cmdUtils. # (see build_websocket_x509_mqtt_connection for implementation) @@ -52,11 +49,7 @@ def on_connection_resumed(connection, return_code, session_present, **kwargs): connect_future = mqtt_connection.connect() # Future.result() waits until a result is available - try: - connect_future.result() - except Exception as e: - print (e) - raise RuntimeError(e) + connect_future.result() print("Connected!") # Disconnect From e8255f4d32d62a617467dfcb30e4df89094154b8 Mon Sep 17 00:00:00 2001 From: Noah Beard Date: Thu, 16 Mar 2023 14:08:29 -0400 Subject: [PATCH 10/11] Add X509 sample README to main sample README --- samples/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/samples/README.md b/samples/README.md index 4af8b24b..0ce36f7a 100644 --- a/samples/README.md +++ b/samples/README.md @@ -10,6 +10,7 @@ * [MQTT5 Custom Authorizer Connect](./mqtt5_custom_authorizer_connect.md) * [Custom Authorizer Connect](./custom_authorizer_connect.md) * [Cognito Connect](./cognito_connect.md) +* [X509 Connect](./x509_connect.md) * [Shadow](./shadow.md) * [Jobs](./jobs.md) * [Fleet Provisioning](./fleetprovisioning.md) From 6b451f43516646b82b3f590416a0ead009c729c1 Mon Sep 17 00:00:00 2001 From: Noah Beard Date: Thu, 16 Mar 2023 16:18:51 -0400 Subject: [PATCH 11/11] Adjust how the sample is written --- samples/utils/command_line_utils.py | 30 ----------- samples/x509_connect.py | 77 +++++++++++++++++++++++++---- 2 files changed, 67 insertions(+), 40 deletions(-) diff --git a/samples/utils/command_line_utils.py b/samples/utils/command_line_utils.py index 6679b70d..46218237 100644 --- a/samples/utils/command_line_utils.py +++ b/samples/utils/command_line_utils.py @@ -244,36 +244,6 @@ def build_websocket_mqtt_connection(self, on_connection_interrupted, on_connecti keep_alive_secs=30) return mqtt_connection - def build_websocket_x509_mqtt_connection(self, on_connection_interrupted, on_connection_resumed): - proxy_options = self.get_proxy_options_for_mqtt_connection() - - x509_tls_options = io.TlsContextOptions.create_client_with_mtls_from_path( - self.get_command_required(self.m_cmd_x509_cert), - self.get_command_required(self.m_cmd_x509_key)) - if (self.get_command(self.m_cmd_x509_ca) != None): - x509_tls_options.ca_dirpath = self.get_command(self.m_cmd_x509_ca) - x509_tls_context = io.ClientTlsContext(x509_tls_options) - - x509_provider = auth.AwsCredentialsProvider.new_x509( - endpoint=self.get_command_required(self.m_cmd_x509_endpoint), - thing_name=self.get_command_required(self.m_cmd_x509_thing_name), - role_alias=self.get_command_required(self.m_cmd_x509_role_alias), - tls_ctx=x509_tls_context, - http_proxy_options=proxy_options - ) - mqtt_connection = mqtt_connection_builder.websockets_with_default_aws_signing( - endpoint=self.get_command_required(self.m_cmd_endpoint), - region=self.get_command_required(self.m_cmd_signing_region), - credentials_provider=x509_provider, - http_proxy_options=proxy_options, - ca_filepath=self.get_command(self.m_cmd_ca_file), - on_connection_interrupted=on_connection_interrupted, - on_connection_resumed=on_connection_resumed, - client_id=self.get_command_required("client_id"), - clean_session=False, - keep_alive_secs=30) - return mqtt_connection - def build_cognito_mqtt_connection(self, on_connection_interrupted, on_connection_resumed): proxy_options = self.get_proxy_options_for_mqtt_connection() diff --git a/samples/x509_connect.py b/samples/x509_connect.py index 535ef953..7bea2bfd 100644 --- a/samples/x509_connect.py +++ b/samples/x509_connect.py @@ -1,15 +1,16 @@ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0. -from awscrt import io +from awscrt import io, http, auth from uuid import uuid4 +from awsiot import mqtt_connection_builder # This sample shows how to create a MQTT connection using X509 files to connect. # This sample is intended to be used as a reference for making MQTT connections via X509. # Parse arguments import utils.command_line_utils as command_line_utils -cmdUtils = command_line_utils.CommandLineUtils("Basic Connect - Make a MQTT connection.") +cmdUtils = command_line_utils.CommandLineUtils("X509 Connect - Make a MQTT connection using X509.") cmdUtils.add_common_mqtt_commands() cmdUtils.add_common_proxy_commands() cmdUtils.add_common_logging_commands() @@ -27,25 +28,81 @@ # Callback when connection is accidentally lost. def on_connection_interrupted(connection, error, **kwargs): - print("Connection interrupted. error: {}".format(error)) + print(f"Connection interrupted. error: {error}") # Callback when an interrupted connection is re-established. def on_connection_resumed(connection, return_code, session_present, **kwargs): - print("Connection resumed. return_code: {} session_present: {}".format(return_code, session_present)) + print(f"Connection resumed. return_code: {return_code} session_present: {session_present}") if __name__ == '__main__': - # Create a connection using X509 authentication to connect - # Note: The data for the connection is gotten from cmdUtils. - # (see build_websocket_x509_mqtt_connection for implementation) - mqtt_connection = cmdUtils.build_websocket_x509_mqtt_connection(on_connection_interrupted, on_connection_resumed) + + ############################################################ + # Pull data from the command line + ############################################################ + input_endpoint = cmdUtils.get_command_required("endpoint") + input_signing_region = cmdUtils.get_command_required("signing_region") + input_ca_file = cmdUtils.get_command("ca_file") + input_client_id = cmdUtils.get_command_required("client_id") + + input_proxy_host = cmdUtils.get_command("proxy_host") + input_proxy_port = cmdUtils.get_command("proxy_port") + + input_x509_endpoint = cmdUtils.get_command_required("x509_endpoint") + input_x509_thing_name = cmdUtils.get_command_required("x509_thing_name") + input_x509_role_alias = cmdUtils.get_command_required("x509_role_alias") + input_x509_cert = cmdUtils.get_command_required("x509_cert") + input_x509_key = cmdUtils.get_command_required("x509_key") + input_x509_ca_file = cmdUtils.get_command("x509_ca_file") + + ############################################################ + # Set up and create the MQTT connection + ############################################################ + + # Set up the config needed to make a MQTT connection + + proxy_options = None + if input_proxy_host is not None and input_proxy_port is not None: + proxy_options = http.HttpProxyOptions( + host_name=input_proxy_host, + port=input_proxy_port) + + x509_tls_options = io.TlsContextOptions.create_client_with_mtls_from_path(input_x509_cert, input_x509_key) + x509_tls_options.ca_dirpath = input_x509_ca_file + x509_tls_context = io.ClientTlsContext(x509_tls_options) + + x509_provider = auth.AwsCredentialsProvider.new_x509( + endpoint=input_x509_endpoint, + thing_name=input_x509_thing_name, + role_alias=input_x509_role_alias, + tls_ctx=x509_tls_context, + http_proxy_options=proxy_options + ) + + # Create the MQTT connection from the configuration + + mqtt_connection = mqtt_connection_builder.websockets_with_default_aws_signing( + endpoint=input_endpoint, + region=input_signing_region, + credentials_provider=x509_provider, + http_proxy_options=proxy_options, + ca_filepath=input_ca_file, + on_connection_interrupted=on_connection_interrupted, + on_connection_resumed=on_connection_resumed, + client_id=input_client_id, + clean_session=False, + keep_alive_secs=30) + + ############################################################ + # Use the MQTT connection to connect and disconnect + ############################################################ if not is_ci: - print("Connecting to {} with client ID '{}'...".format( - cmdUtils.get_command(cmdUtils.m_cmd_endpoint), cmdUtils.get_command("client_id"))) + print (f"Connecting to {input_endpoint} with client ID '{input_client_id}'...") else: print("Connecting to endpoint with client ID") + # Connect connect_future = mqtt_connection.connect() # Future.result() waits until a result is available