Hardening Azure Event Hub Solutions

Allgemein

In vielen IoT Scenarien stellt sich immer wieder die Frage wie der Daten Ingest von Devices (Things) zu einem zentralen Backend abgesichert werden kann. Hierbei ist neben der grundsätzlichen Absicherung des Transportweges auch die Absicherung des Dateningest einzelner Devices zu verstehen. Verwendet man den Azure IoT Hub kann jedes Device in dem zentralen Repository angelegt und mit entsprechenden Security Credentials versorgt werden. Dadurch ist eine feingranulare Verwaltung von Devices möglich. Letztendlich kann dadurch auf Device Ebene entschieden werden, ob die Daten des Device akzeptiert werden oder ggf. als kompromitiert betrachtet und verworfen werden.

Azure Event Hub stellt eine ähnliche Funktionalität zur Verfügung welche ebenfalls ermöglicht, den Daten Ingest von einzelnen Devices bereits auf Ebene des Event Hub zu blockieren.

Shared Access Policy

Nach Anlage eines Event Hub im Azure Portal können sogenannte „Shared Access Policies“ angelegt werden. Zu jeder Shared Access Policy werden zwei Keys erzeugt welche verwendet werden können, um ein Datenpaket zu signieren. D. h. Datenpakete welche von einem Device zu Azure Event Hub übertragen werden verwenden einen dieser Keys um einen Hash des Datenpaketes zu errechnen. Dieser Hash wird mit einem Key der erzeugten Shared Access Policy verschlüssen und zusammen mit anderen Informationen wie z. B. Event Hub Name und  Shared Access Policy Name zu einem String zusammengefasst. Dieser String wird nun jedem Datenpaket welches vom Device zum Event Hub übertragen wird beigefügt. Vereinfacht gesprochen handelt es sich bei diesem String um eine „Eintrittskarte“ welche das Datenpaket gegenüber dem Event Hub ausweist. Der Event Hub kann mittels der im String enthaltenen Informationen und einem privatem Schlüssel, der während der Anlage der Shared Access Policy ebenfalls erzeugt wurde und im Portal nicht sichtbar ist, prüfen ob die Signatur korrekt und damit das Datenpaket valide ist.

Der Key einer Shared Access Policy muss dementsprechend vertraulich behandelt werden. Viele Beispiele zu Übertragung von Daten von einem Device zu Event Hub verwenden jedoch den Key im Code des Devices. Insbesondere die Methode ServiceBusConnectionStringBuilder.CreateUseingSharedAccessKey() des aktuellen Azure Service Bus SDK (Azure Event Hub ist Teil des Azure Service Bus) macht es sehr einfach, auf Basis eines Shared Access Policy Key einen Connection String zum Event Hub zu erzeugen.

      

        public void TelemetryIngest()
        {
            string serviceBusNamespace = "<<Your Service Bus Namespace>> e. g. -> iotmc-ns";
            string eventHubName = "<<your Event Hub Name>> e. g. -> IoTMC";
            string eventHubSAPKeyName = "<<your Shared Access Policy Name>> e. g. -> Device01";
            string eventHubSAPKey = "<<your Key>> e.g. -> t0JK197745t345…s4N09aNI0s=";
            string eventHubConnectionString = ServiceBusConnectionStringBuilder.CreateUsingSharedAccessKey(
            ServiceBusEnvironment.CreateServiceUri("sb", serviceBusNamespace, string.Empty),
                eventHubSAPKeyName,
                eventHubSAPKey);
            EventHubClient eventHubClient = EventHubClient.CreateFromConnectionString(eventHubConnectionString, eventHubName);
            //Your code to ingest telemetry data
        }

Wurde das Device kompromitiert, muss serverseiting ein neuer Key generiert werden, damit Event Hub zukünftig Telemetry Ingests von diesem Device ablehnt. Dies ist solange machbar, wie eine geringe Anzahl von Devices installiert worden sind und für jedes Device eine eigene Shared Access Policy erzeugt wurde. Jedoch ist die Anzahl der erzeugbaren Shared Access Policies begrenzt, so dass mehrere (unter Umständen hunderte oder tausende) Devices mit dem Key einer einzigen Shared Access Policy Key provisioniert werden müssen. Wird nun ein einzelnes Device kompromitiert müssen unter Umständen hunderte oder tausende weitere Devices aktualisiert bzw. ausgetauscht werden.

Betrachtet man z. B. Devices die an einem Fließband z. B. die Tätigkeit von Robotern überwachen übermitteln so können diese in einer einfachen Anbindung den Key einer Shared Access Policy benützen, um Daten gegenüber einem Event Hub zu legitimitieren. In der Regel finden sich an einem Fließband sehr viele Sensoren und Devices so dass ein Key einer Shared Access Policy von ggf. vielen Devices verwendet werden muss.

Um diese Problematik zu adressieren, bietet sich eine Architektur an, bei der die Keys nicht auf dem Device gespeichert werden und sogenannte Shared Access Signature Tokens (SAS Token) „off-board“ erzeugt werden, die einen zeitlich begrenzten Daten-Ingest von den Devices erlauben.

„Off-Board“ Token Erzeugung

Bei der Erzeugung eines SAS Tokens „off-Board“ wird der Key der Shared Access Policy nicht auf den Devices  vorgehalten sondern von einem externen Service verwendet um ein SAS Token zu erzeugen, dass ebenfalls zur Legitimitierung von Telemetry Ingest am Event Hub verwendet werden kann. Diese Tokens beinhalten eine temporäre Komponente, d. h. sie sind nur für eine bestimmte definierbare Zeitspanne zur Legitimitierung am Event Hub gültig. Ein typischer Workflow bei der „off-Board“ Token Erzeugung stellt sich folgendermaßen dar:

  1. Device kontaktiert einen externen Service, in der Regel ein REST basierter Service (Discovery Service) und authorisiert sich mit den üblichen Verfahren (Pre-Shared-Key, OAuth etc.) I.d.R. werden hier noch zusätzliche Metadaten übertragen und die vom Discovery Service geprüft. Z. B. aktuelle IP Adresse oder GPS Daten um zu prüfen, ob das Device sich noch an der vorgesehen physikalischen oder netzwerktechnischen Position befindet. Sehr häufig findet sich hier auch eine Prüfung des letzten Daten Ingest. Diese Information kann im Backend ermittelt werden und ist hier bereits eine gewisse Zeitspanne seit dem letzen Daten Ingest verstrichen und es sind keine Wartungsarbeiten, Bandstillstand etc. aufgetreten kann dieses Device als zumindest „wartungsbedürftig“ oder im extremen Fall als kompromitiert betrachtet werden. In diesem Fall wird kein Token vom Service ausgestellt. Ohne gültiges Token ist auch kein Telemetry Ingest am Event Hub möglich.
  2. Ist die Authorisierung und Prüfung der Metadaten erfolgreich, kann der Discovery Service ein Token für das jeweilige Device erstellen und als Return Value eines potentiellen REST Call an das Device übermittelt werden. Die detailierte Beschreibung, wie dieses Token erzeugt wird, findet sich im weiteren Verlauf des Artikels.
  3. Das Device verwendet das erzeugte Token beim Telemetry Ingest an den Event Hub um diesen zu legitimitieren.

Erzeugung Shared Access Signature (SAS) Token

Ein SAS Token beinhaltet neben der bereits erwähnten zeitlichen Komponente zusätzlich die Möglichkeit ein einzelnes Device eindeutig zu identifizieren. Dies kann über einen String z. B. eine Guid oder einen beliebige andere Kennung erfolgen. Im SDK wird hierbei von einem Publisher gesprochen. Die Erzeugung eines SAS Token gestaltet sich nun mit Hilfe des Service Bus SDK sehr einfach. Die Methode SharedAccessSigantureTokenProvider.GetPublisherSharedAccessSignature() erzeugt ein entsprechendes SAS Token:

        string CreateSASToken()
        {
            Uri serviceBusEndPoint = new Uri("<<your service bus endpoint e.g. -> sb://iotvd-ns.servicebus.windows.net/");
            string eventHubName = "<<your Event Hub Name>> e.g. -> IoTMC"; ;
            string publisherId = "<<your device publisher id e.g. -> DeviceLocatedAtMachine01";
            string eventHubSAPKeyName = "<<your Shared Access Policy Name>> e. g. -> send";
            string eventHubSAPKey = "<<your Key>> e.g. -> t0JK197745t345…s4N09aNI0s=";
            TimeSpan ingestValidTimeSpan = new TimeSpan(0, 5, 0);

            return SharedAccessSignatureTokenProvider.GetPublisherSharedAccessSignature(
                            serviceBusEndPoint, 
                            eventHubName, 
                            publisherId, 
                            eventHubSAPKeyName, 
                            eventHubSAPKey, 
                            ingestValidTimeSpan);
      }

Auf dem Device kann das erzeugte Token nun verwendet werden um einen Telemetry Ingest zum Event Hub durchzuführen:

void IngestTelemetry()
 {
Uri serviceBusEndPoint = new Uri("< sb://iotvd-ns.servicebus.windows.net/");
 string eventHubName = "<> e.g. -> IoTMC";
 string publisherId = "< DeviceLocatedAtMachine01";
 string sASToken = "<>";
string eventHubConnectionString = ServiceBusConnectionStringBuilder.CreateUsingSharedAccessSignature(
serviceBusEndPoint,
eventHubName,
publisherId,
sAStoken);
var eventHubSender = EventHubSender.CreateFromConnectionString(eventHubConnectionString);
eventHubSender.Send(new EventData(Encoding.UTF8.GetBytes("<>>")));

Das Device kann nun bis zum Ablauf der vom Discovery Service gewählten Zeitspanne das SAS Token benützen, um Daten an den Event Hub zu senden. Sobald diese Zeitspanne abgelaufen ist wird das Token ungültig und beim Senden der Telemetry Daten tritt eine Exception auf. In diesem Fall kann das Device wieder den Discovery Service kontaktieren und ein neues SAS Token anfordern. Sollte der Discovery Service Unregelmäßigkeiten feststellen, kann er diese Ausstellung verweigern und der Telemetry Ingest von evtl. kompromitierten oder fehlerhaften Devices somit unterbinden.

 

Blockierung von einzelnen Devices

Solange die Zeitspanne die bei der Erzeugung eines SAS Tokens angegeben worden ist, kann dieses Token verwendet werden um Daten an einen Event Hub zu senden. Es gibt keine Möglichkeit, ein Token vor Ablauf der Gültigkeit als ungültig zu kennzeichnen und damit einen Telemetry Ingest zu unterbinden.

Um trotzdem den Ingest von einem als fehlerhaft oder kompromitiert eingestuftem Device vor Ablauf der Token Gültigkeit zu unterbinden kann die Publisher Id des jeweiligen Device verwendet werden. Diese Publisher Id wird der Methode ServiceBsConnectionStringBuilder.CreateUsingSharedAccessSignature() übergeben um einen ConnectionString zum Event Hub zu erzeugen.

        
        void IngestTelemetry()
        {

            Uri serviceBusEndPoint = new Uri("<<your service bus endpoint e.g. -> sb://iotvd-ns.servicebus.windows.net/");
            string eventHubName = "<<your Event Hub Name>> e.g. -> IoTMC";
            string publisherId = "<<your device publisher id e.g. -> DeviceLocatedAtMachine01";
 string sASToken = "<<Token generated by Discovery Service>>";
 
            string eventHubConnectionString = ServiceBusConnectionStringBuilder.CreateUsingSharedAccessSignature(
                serviceBusEndPoint, 
                eventHubName, 
                publisherId, 
                sAStoken);

            var eventHubSender = EventHubSender.CreateFromConnectionString(eventHubConnectionString);

            eventHubSender.Send(new EventData(Encoding.UTF8.GetBytes("<<Your Telemetry Data")));
        }

Diese Publisher Id kann wie bereits erwähnt beliebig gewählt werden und sollte jedes Device eindeuting kennzeichnen.

Soll nun der Telemetry Ingest von einem bestimmten Device vor Ablauf der Token Lebenszeit gesperrt werden, kann dies über die Methode RevokePublisher()  einer Instanz des Objektes NamespaceManger des Service Bus SDK erreicht werden. Zu beachten ist, dass hierfür eine Verbindung zum Service Bus Namespace in dem der Event Hub angelegt wurde notwendig ist. Eine Verbindung zum jeweiligen Event Hub auf dem der Telemetry Ingest eines Devices blockiert werden soll ist hierfür nicht ausreichend. Die Verbindung muss zusätzlich über die Berechtigung „manage“ verfügen. Am einfachsten ist es hierbei den notwendigen Connection String direkt aus dem Azure Portal zu kopieren. Nach erfolgreicher Instanzierung eines Namespace Manger Objektes können die Methoden RevokePublisher() und RestorePublisher() verwendet werden, um den Telemetry Ingest zu blockieren oder nach bereits erfolgter Blockierung wieder zuzulassen. Beide Methoden erwarten hierfür zum einen den Namen des Event Hub und die jeweilige Publisher Id des Devices:

        void IngestTelemetry()
        {
            Uri serviceBusEndPoint = new Uri("<<your service bus endpoint e.g. -> sb://iotvd-ns.servicebus.windows.net/");
            string eventHubName = "<<your Event Hub Name>> e.g. -> IoTMC";
            string publisherId = "<<your device publisher id e.g. -> DeviceLocatedAtMachine01";
            string sASToken = "<<Token generated by Discovery Service>>";
 
            string eventHubConnectionString = ServiceBusConnectionStringBuilder.CreateUsingSharedAccessSignature(
                serviceBusEndPoint, 
                eventHubName, 
                publisherId, 
                sAStoken);
            var eventHubSender = EventHubSender.CreateFromConnectionString(eventHubConnectionString);
            eventHubSender.Send(new EventData(Encoding.UTF8.GetBytes("<<Your Telemetry Data")));
        }

Fazit

Auch wenn es auf den ersten Blick kofortabel und schnell möglich ist, den Key einer Shared Access Policy direkt in einem Device zu verwenden, sollte man bei der Konzeption einer IoT Lösung einen sogenannten Discovery Service mit der „off-board“ Erzeugung von sogenannten Shared Access Policy Tokens berücksichtigen. Die dadurch gewonnenen Möglichkeiten eine IoT Lösung abzusichern wiegen in der Regel den zusätlichen Aufwand und die zusätzlichen Kosten zur Betreibung/Wartung des Discovery Service auf.