Skip to content

Commit

Permalink
Add E2E tests for X.509 auth (#703)
Browse files Browse the repository at this point in the history
Changes here implement E2E tests for downstream devices to authenticate with the Edge using X.509 certificates. Additional noteworthy change is that the EdgeHub enables X.509 auth to ON by default.
  • Loading branch information
mrohera authored Jan 14, 2019
1 parent 8bd5735 commit 4a46290
Show file tree
Hide file tree
Showing 7 changed files with 315 additions and 61 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
Expand Down Expand Up @@ -302,6 +302,9 @@ target/
# IoT Edge artifacts
backup.json

# IoT Edge formatting artifacts
stylecop.json

# python
**/*.egg-info
**/.coverage
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
"timeToLiveSecs": "-1"
},
"RuntimeLogLevel": "info",
"ClientCertAuthEnabled": false,
"ClientCertAuthEnabled": true,
"OptimizeForPerformance": true,
"ConnectivityCheckFrequencySecs": 300,
"MaxConnectedClients": 100,
Expand Down
68 changes: 61 additions & 7 deletions smoke/LeafDevice/LeafDevice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,72 @@
namespace LeafDevice
{
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using global::LeafDevice.Details;
using Microsoft.Azure.Devices.Edge.Util;

public class LeafDevice : Details.Details
{
public LeafDevice(
string iothubConnectionString,
string eventhubCompatibleEndpointWithEntityPath,
string deviceId,
string certificateFileName,
string trustedCACertificateFileName,
string edgeHostName,
bool useWebSockets)
: base(iothubConnectionString, eventhubCompatibleEndpointWithEntityPath, deviceId, certificateFileName, edgeHostName, useWebSockets)
: base(
iothubConnectionString,
eventhubCompatibleEndpointWithEntityPath,
deviceId,
trustedCACertificateFileName,
edgeHostName,
useWebSockets,
Option.None<DeviceCertificate>(),
Option.None<IList<string>>())
{
}

public LeafDevice(
string iothubConnectionString,
string eventhubCompatibleEndpointWithEntityPath,
string deviceId,
string trustedCACertificateFileName,
string edgeHostName,
bool useWebSockets,
string clientCertificatePath,
string clientCertificateKeyPath)
: base(
iothubConnectionString,
eventhubCompatibleEndpointWithEntityPath,
deviceId,
trustedCACertificateFileName,
edgeHostName,
useWebSockets,
Option.Some(new DeviceCertificate { CertificateFilePath = clientCertificatePath, PrivateKeyFilePath = clientCertificateKeyPath }),
Option.None<IList<string>>())
{
}

public LeafDevice(
string iothubConnectionString,
string eventhubCompatibleEndpointWithEntityPath,
string deviceId,
string trustedCACertificateFileName,
string edgeHostName,
bool useWebSockets,
string clientCertificatePath,
string clientCertificateKeyPath,
IList<string> thumprintCertificates)
: base(
iothubConnectionString,
eventhubCompatibleEndpointWithEntityPath,
deviceId,
trustedCACertificateFileName,
edgeHostName,
useWebSockets,
Option.Some(new DeviceCertificate { CertificateFilePath = clientCertificatePath, PrivateKeyFilePath = clientCertificateKeyPath }),
Option.Some(thumprintCertificates))
{
}

Expand All @@ -22,11 +76,11 @@ public async Task RunAsync()
// This test assumes that there is an edge deployment running as transparent gateway.
try
{
await this.InitializeServerCerts();
await this.GetOrCreateDeviceIdentity();
await this.ConnectToEdgeAndSendData();
await this.VerifyDataOnIoTHub();
await this.VerifyDirectMethod();
await this.InitializeTrustedCertsAsync();
await this.GetOrCreateDeviceIdentityAsync();
await this.ConnectToEdgeAndSendDataAsync();
await this.VerifyDataOnIoTHubAsync();
await this.VerifyDirectMethodAsync();
}
catch (Exception)
{
Expand Down
89 changes: 79 additions & 10 deletions smoke/LeafDevice/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
namespace LeafDevice
{
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using McMaster.Extensions.CommandLineUtils;
using Microsoft.Azure.Devices.Edge.Util.Test.Common;
Expand Down Expand Up @@ -40,8 +41,8 @@ class Program
[Option("-e|--eventhub-endpoint <value>", Description = "Event Hub-compatible endpoint for IoT Hub, including EntityPath")]
public string EventHubCompatibleEndpointWithEntityPath { get; } = Environment.GetEnvironmentVariable("eventhubCompatibleEndpointWithEntityPath");

[Option("-ct|--certificate <value>", Description = "Certificate file to be installed on the machine.")]
public string CertificateFileName { get; } = string.Empty;
[Option("-ct|--certificate <value>", Description = "Trust bundle CA Certificate(s) file to be installed on the machine.")]
public string TrustedCACertificateFileName { get; } = string.Empty;

[Option("-d|--device-id", Description = "Leaf device identifier to be registered with IoT Hub")]
public string DeviceId { get; } = $"leaf-device--{Guid.NewGuid()}";
Expand All @@ -52,6 +53,24 @@ class Program
[Option("--use-web-sockets", CommandOptionType.NoValue, Description = "Use websockets for IoT Hub connections.")]
public bool UseWebSockets { get; } = false;

[Option("-cac|--x509-ca-cert-path", Description = "Path to a X.509 leaf certificate file in PEM format to be used for X.509 CA authentication.")]
public string X509CACertPath { get; } = string.Empty;

[Option("-cak|--x509-ca-key-path", Description = "Path to a X.509 leaf certificate key file in PEM format to be used for X.509 CA authentication.")]
public string X509CAKeyPath { get; } = string.Empty;

[Option("-ctpc|--x509-primary-cert-path", Description = "Path to a X.509 leaf certificate file in PEM format. This is needed for thumbprint auth and used as the primary certificate.")]
public string X509PrimaryCertPath { get; } = string.Empty;

[Option("-ctpk|--x509-primary-key-path", Description = "Path to a X.509 leaf certificate key file in PEM format. This is needed for thumbprint auth and used as the primary certificate's key.")]
public string X509PrimaryKeyPath { get; } = string.Empty;

[Option("-ctsc|--x509-secondary-cert-path", Description = "Path to a X.509 leaf certificate file in PEM format. This is needed for thumbprint auth and used as the secondary certificate.")]
public string X509SecondaryCertPath { get; } = string.Empty;

[Option("-ctsk|--x509-secondary-key-path", Description = "Path to a X.509 leaf certificate key file in PEM format. This is needed for thumbprint auth and used as the secondary certificate's key.")]
public string X509SecondaryKeyPath { get; } = string.Empty;

// ReSharper disable once UnusedMember.Local
static int Main(string[] args) => CommandLineApplication.ExecuteAsync<Program>(args).Result;

Expand All @@ -66,14 +85,64 @@ async Task<int> OnExecuteAsync()
string endpoint = this.EventHubCompatibleEndpointWithEntityPath ??
await SecretsHelper.GetSecretFromConfigKey("eventHubConnStrKey");

var test = new LeafDevice(
connectionString,
endpoint,
this.DeviceId,
this.CertificateFileName,
this.EdgeHostName,
this.UseWebSockets);
await test.RunAsync();
if (!string.IsNullOrWhiteSpace(this.X509PrimaryCertPath) &&
!string.IsNullOrWhiteSpace(this.X509PrimaryKeyPath) &&
!string.IsNullOrWhiteSpace(this.X509SecondaryCertPath) &&
!string.IsNullOrWhiteSpace(this.X509SecondaryKeyPath))
{
// use thumbprint auth and perform test for both primary and secondary certificates
var thumbprintCerts = new List<string> { this.X509PrimaryCertPath, this.X509SecondaryCertPath };
var testPrimaryCertificate = new LeafDevice(
connectionString,
endpoint,
this.DeviceId,
this.TrustedCACertificateFileName,
this.EdgeHostName,
this.UseWebSockets,
this.X509PrimaryCertPath,
this.X509PrimaryKeyPath,
thumbprintCerts);
await testPrimaryCertificate.RunAsync();

var testSeondaryCertificate = new LeafDevice(
connectionString,
endpoint,
this.DeviceId,
this.TrustedCACertificateFileName,
this.EdgeHostName,
this.UseWebSockets,
this.X509SecondaryCertPath,
this.X509SecondaryKeyPath,
thumbprintCerts);
await testSeondaryCertificate.RunAsync();
}
else if (!string.IsNullOrWhiteSpace(this.X509CACertPath) &&
!string.IsNullOrWhiteSpace(this.X509CAKeyPath))
{
// use X.509 CA auth and perform test using CA chained certificates
var testCa = new LeafDevice(
connectionString,
endpoint,
this.DeviceId,
this.TrustedCACertificateFileName,
this.EdgeHostName,
this.UseWebSockets,
this.X509CACertPath,
this.X509CAKeyPath);
await testCa.RunAsync();
}
else
{
// non certificate flow use SAS tokens
var testSas = new LeafDevice(
connectionString,
endpoint,
this.DeviceId,
this.TrustedCACertificateFileName,
this.EdgeHostName,
this.UseWebSockets);
await testSas.RunAsync();
}
}
catch (Exception ex)
{
Expand Down
Loading

0 comments on commit 4a46290

Please sign in to comment.