diff --git a/documents/Secure_Tunnel_Userguide.md b/documents/Secure_Tunnel_Userguide.md index acf8b346b..6a5e76237 100644 --- a/documents/Secure_Tunnel_Userguide.md +++ b/documents/Secure_Tunnel_Userguide.md @@ -1,4 +1,22 @@ # Secure Tunnel Guide +#Table of Contents +* [Introduction](#introduction) +* [Getting Started with Secure Tunnels](#getting-started-with-secure-tunnels) + + [How to Create a Secure Tunnel Client](#how-to-create-a-secure-tunnel-client) +* [Callbacks](#callbacks) + + [Setting Secure Tunnel Callbacks](#setting-secure-tunnel-callbacks) + + [Available Callbacks](#available-callbacks) +* [Connecting and Disconnecting the Secure Tunnel Client](#connecting-and-disconnecting-the-secure-tunnel-client) + + [Start (Connect)](#start) + + [Stop (Disconnect)](#stop) +* [Multiplexing and Simultaneous TCP Connections](#multiplexing-and-simultaneous-tcp-connections) + + [Opening a Secure Tunnel with Multiplexing](#opening-a-secure-tunnel-with-multiplexing) + + [Service Ids Available on the Secure Tunnel](#service-ids-available-on-the-secure-tunnel) + + [Using Service Ids](#using-service-ids) + + [Using Connection Ids](#using-connection-ids) +* [Send Message](#send-message) +* [Secure Tunnel Best Practices/FAQ](#secure-tunnel-best-practices) + ## Introduction When devices are deployed behind restricted firewalls at remote sites, you need a way to gain access to those devices for troubleshooting, configuration updates, and other operational tasks. Use secure tunneling to establish bidirectional communication to remote devices over a secure connection that is managed by AWS IoT. Secure tunneling does not require updates to your existing inbound firewall rules, so you can keep the same security level provided by firewall rules at a remote site. @@ -32,8 +50,40 @@ if (!secureTunnel->Start()) return -1; } ``` -## Callbacks +# Callbacks +## Setting Secure Tunnel Callbacks +The Secure Tunnel client uses callbacks to keep the user updated on its status and pass along relavant information. These can be set up using the Secure Tunnel builder's With functions. + +```cpp +// Create Secure Tunnel Builder +SecureTunnelBuilder builder = SecureTunnelBuilder(...); + +// Setting the onMessageReceived callback using the builder +builder.WithOnMessageReceived([&](SecureTunnel *secureTunnel, const MessageReceivedEventData &eventData) { + { + std::shared_ptr message = eventData.message; + if (message->getServiceId().has_value()){ + fprintf( + stdout, + "Message received on service id:'" PRInSTR "'\n", + AWS_BYTE_CURSOR_PRI(message->getServiceId().value())); + } + + if(message->getPayload().has_value()){ + fprintf( + stdout, + "Message has payload:'" PRInSTR "'\n", + AWS_BYTE_CURSOR_PRI(message->getPayload().value())); + } + } + }); + +// Build Secure Tunnel Client +std::shared_ptr secureTunnel = builder.Build(); +// Messages received on a stream will now be printed to stdout. +``` +## Available Callbacks ### OnConnectionSuccess When the Secure Tunnel Client successfully connects with the Secure Tunnel service, this callback will return the available (if any) service ids. @@ -67,53 +117,7 @@ When the Secure Tunnel service requests the Secure Tunnel client fully reset, th ### OnStopped When the Secure Tunnel has reached a fully stopped state this callback is invoked. -## Setting Secure Tunnel Callbacks -The Secure Tunnel client uses callbacks to keep the user updated on its status and pass along relavant information. These can be set up using the Secure Tunnel builder's With functions. - -```cpp -// Create Secure Tunnel Builder -SecureTunnelBuilder builder = SecureTunnelBuilder(...); - -// Setting the onMessageReceived callback using the builder -builder.WithOnMessageReceived([&](SecureTunnel *secureTunnel, const MessageReceivedEventData &eventData) { - { - std::shared_ptr message = eventData.message; - if (message->getServiceId().has_value()){ - fprintf( - stdout, - "Message received on service id:'" PRInSTR "'\n", - AWS_BYTE_CURSOR_PRI(message->getServiceId().value())); - } - - if(message->getPayload().has_value()){ - fprintf( - stdout, - "Message has payload:'" PRInSTR "'\n", - AWS_BYTE_CURSOR_PRI(message->getPayload().value())); - } - } - }); - -// Build Secure Tunnel Client -std::shared_ptr secureTunnel = builder.Build(); - -if (secureTunnel == nullptr) -{ - fprintf(stdout, "Secure Tunnel creation failed.\n"); - return -1; -} - -// Start the secure tunnel connection -if (!secureTunnel->Start()) -{ - fprintf("Failed to start Secure Tunnel\n"); - return -1; -} - -// Messages received on a stream will now be printed to stdout. -``` - -# How to Start and Stop +# Connecting and Disconnecting the Secure Tunnel Client ## Start Invoking `Start()` on the Secure Tunnel Client will put it into an active state where it recurrently establishes a connection to the configured Secure Tunnel endpoint using the provided [Client Access Token](https://docs.aws.amazon.com/iot/latest/developerguide/secure-tunneling-concepts.html). If a [Client Token](https://docs.aws.amazon.com/iot/latest/developerguide/secure-tunneling-concepts.html) is provided, the Secure Tunnel Client will use it. If a Client Token is not provided, the Secure Tunnel Client will automatically generate one for use on a reconnection attempts. The Client Token for any initial connection to the Secure Tunnel service **MUST** be unique. Reusing a Client Token from a previous connection will result in a failed connection to the Secure Tunnel Service. @@ -157,7 +161,7 @@ You can use multiple data streams per Secure Tunnel by using the [Multiplexing a ## Opening a Secure Tunnel with Multiplexing To use Multiplexing, a Secure Tunnel must be created with one to three "services". A Secure Tunnel can be opened through the AWS IoT console [Secure Tunnel Hub](https://console.aws.amazon.com/iot/home#/tunnelhub) or by using the [OpenTunnel API](https://docs.aws.amazon.com/iot/latest/apireference/API_Operations_AWS_IoT_Secure_Tunneling.html). Both of these methods allow you to add services with whichever names suit your needs. -## Services Within the Secure Tunnel Client +## Service Ids Available on the Secure Tunnel On a successfull connection to a Secure Tunnel, the Secure Tunnel Client will invoke the `OnConnectionSuccess` callback. This callback will return `ConnectionSuccessEventData` which contains any available Service Ids that can be used for multiplexing. Below is an example of how to set the callback using the Secure Tunnel Builder and check whether a Service Id is available. ```cpp // Create Secure Tunnel Builder @@ -202,9 +206,8 @@ builder.WithOnConnectionSuccess([&](SecureTunnel *secureTunnel, const Connection Service Ids can be added to outbound Messages as shown below in the Send Message example. If the Service Id is both available on the current Secure Tunnel and there is an open stream with a Source device on that Service Id, the message will be sent. If the Service Id does not exist on the current Secure Tunnel or there is no currently active stream available on that Service Id, the Message will not be sent and a Warning will be logged. The `OnStreamStarted` callback is invoked when a stream is started and it returns a `StreamStartedEventData` which can be parsed to determine if a stream was started using a Service Id for Multiplexing. Incoming messages can also be parsed to determine if a Service Id has been set as shown above in the [Setting Secure Tunnel Callbacks](#setting-secure-tunnel-callbacks) code example. ## Using Connection Ids Connection Ids can be added to outbound Messages as shown below in the Send Message example. If there is an active stream currently open using the combination of the Service Id and Connection Id, the message will be sent. If a Connection Id is not set on an outbound message, a Connecion Id of 1 is assumed and applied to the Message. When additional streams are activated, the `OnConnectionStarted` callback is invoked and returns a `ConnectionStartedEventData` which can be parsed to determine the Connection Id of the newly activated stream. A Connection Id will also be present in the `StreamStartedEventData` that is returned when the `OnStreamStarted` callback is invoked. -# Secure Tunnel Operations -## Send Message +# Send Message The `SendMessage()` operation takes a description of the Message you wish to send and returns a success/failure in the synchronous logic that kicks off the `SendMessage()` operation. When the message is fully written to the socket, the `OnSendDataComplete` callback will be invoked. ```cpp @@ -232,3 +235,6 @@ secureTunnel->SendMessage(message); * If you do not provide a Client Token during creation of the Secure Tunnel, one will be automatically generated for you to use in reconnections. This token is not saved outside of the current Secure Tunnel Client. If the Client is destroyed, the original access tokens must be rotated to connect to the secure tunnel again. Information on rotating tokens can be found here: https://docs.aws.amazon.com/iot/latest/developerguide/iot-secure-tunneling-troubleshooting.html * Client tokens MUST be unique. You cannot for example, pair a Client Token with an Access Token on one secure tunnel, and then use the same Client Token with a different Access Token on a separate secure tunnel. The Secure Tunnel Service will not allow a Client Token to be paired with more than one Access Token. * A Secure Tunnel Client that has called `Start()` will continue to attempt to connect the Secure Tunnel Service until `Stop()` is called, even if the Secure Tunnel it is trying to connect with has been closed. You MUST call `Stop()` to cease future connection attempts. +* The [onStreamStarted](#onstreamstarted) and [onConnectionStarted](#onconnectionstarted) callbacks should be set to detect and store the service id and/or connection id of streams started by a source device for use with messages. The [Secure Tunnel sample](../samples/secure_tunneling/secure_tunnel/README.md) provides an basic example of how this can be done. +* Outgoing messages MUST be assigned a service id and/or a connection id if the established stream contains a service id or a connection id or the message will be rejected. e.g. If a stream is started using service id "ssh" and connection id (1), a message sent in response must also include the service id "ssh" and connection id (1) or it will not find an active stream to send it on. Refer to the [Send Message](#send-message) code block for instruction on adding a service id and/or connection id to your message. +