From 6397a9c39fc8baaeb1b26c24375a8801fe973786 Mon Sep 17 00:00:00 2001 From: Erich Barnstedt Date: Mon, 10 Jul 2017 12:43:03 +0200 Subject: [PATCH 1/2] Make tracing better --- .gitignore | 3 +- src/ModuleConfiguration.cs | 19 ++- src/Program.cs | 299 +++++++++++++++++++------------------ 3 files changed, 166 insertions(+), 155 deletions(-) diff --git a/.gitignore b/.gitignore index cd25137..dbd3083 100644 --- a/.gitignore +++ b/.gitignore @@ -17,7 +17,8 @@ *.dll *.so *.pdb -/src/GatewayApp.NetCore/Logs *.der *.exe /src/out +/src/Logs + diff --git a/src/ModuleConfiguration.cs b/src/ModuleConfiguration.cs index a45f89b..842e04d 100644 --- a/src/ModuleConfiguration.cs +++ b/src/ModuleConfiguration.cs @@ -22,6 +22,19 @@ namespace Opc.Ua.Publisher Configuration.ClientConfiguration = new ClientConfiguration(); Configuration.ServerConfiguration = new ServerConfiguration(); + // enable logging + Configuration.TraceConfiguration = new TraceConfiguration(); + Configuration.TraceConfiguration.TraceMasks = Utils.TraceMasks.Error | Utils.TraceMasks.Security | Utils.TraceMasks.StackTrace | Utils.TraceMasks.StartStop; + if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("_GW_LOGP"))) + { + Configuration.TraceConfiguration.OutputFilePath = Environment.GetEnvironmentVariable("_GW_LOGP"); + } + else + { + Configuration.TraceConfiguration.OutputFilePath = "./Logs/" + Configuration.ApplicationName + ".log.txt"; + } + Configuration.TraceConfiguration.ApplySettings(); + if (Configuration.SecurityConfiguration == null) { Configuration.SecurityConfiguration = new SecurityConfiguration(); @@ -161,12 +174,6 @@ namespace Opc.Ua.Publisher newPolicy.SecurityPolicyUri = SecurityPolicies.Basic128Rsa15; Configuration.ServerConfiguration.SecurityPolicies.Add(newPolicy); - // enable logging - Configuration.TraceConfiguration = new TraceConfiguration(); - Configuration.TraceConfiguration.TraceMasks = Utils.TraceMasks.Error | Utils.TraceMasks.Security | Utils.TraceMasks.StackTrace | Utils.TraceMasks.StartStop; - Configuration.TraceConfiguration.OutputFilePath = "./Logs/" + Configuration.ApplicationName + ".log.txt"; - Configuration.TraceConfiguration.ApplySettings(); - // the OperationTimeout should be twice the minimum value for PublishingInterval * KeepAliveCount, so set to 120s Configuration.TransportQuotas.OperationTimeout = 120000; diff --git a/src/Program.cs b/src/Program.cs index 3b5e073..7e20dc4 100644 --- a/src/Program.cs +++ b/src/Program.cs @@ -29,192 +29,195 @@ namespace Opc.Ua.Publisher /// public static void Trace(string message, params object[] args) { - Utils.Trace(message, args); - Console.WriteLine(message, args); + Utils.Trace(Utils.TraceMasks.Error, message, args); + Console.WriteLine(DateTime.Now.ToString() + ": " + message, args); } public static void Trace(int traceMask, string format, params object[] args) { Utils.Trace(traceMask, format, args); - Console.WriteLine(format, args); + Console.WriteLine(DateTime.Now.ToString() + ": " + format, args); } public static void Trace(Exception e, string format, params object[] args) { Utils.Trace(e, format, args); - Console.WriteLine(e.ToString()); - Console.WriteLine(format, args); + Console.WriteLine(DateTime.Now.ToString() + ": " + e.Message.ToString()); + Console.WriteLine(DateTime.Now.ToString() + ": " + format, args); } public static void Main(string[] args) { - if ((args.Length == 0) || string.IsNullOrEmpty(args[0])) + try { - Console.WriteLine("Please specify an application name as argument!"); - return; - } - - m_applicationName = args[0]; - string ownerConnectionString = string.Empty; - - // check if we also received an owner connection string - if ((args.Length > 1) && !string.IsNullOrEmpty(args[1])) - { - ownerConnectionString = args[1]; - } - else - { - Trace("IoT Hub owner connection string not passed as argument."); - - // check if we have an environment variable to register ourselves with IoT Hub - if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("_HUB_CS"))) + if ((args.Length == 0) || string.IsNullOrEmpty(args[0])) { - ownerConnectionString = Environment.GetEnvironmentVariable("_HUB_CS"); - } - } - - // register ourselves with IoT Hub - if (ownerConnectionString != string.Empty) - { - Trace("Attemping to register ourselves with IoT Hub using owner connection string: " + ownerConnectionString); - RegistryManager manager = RegistryManager.CreateFromConnectionString(ownerConnectionString); - - // remove any existing device - Device existingDevice = manager.GetDeviceAsync(m_applicationName).Result; - if (existingDevice != null) - { - manager.RemoveDeviceAsync(m_applicationName).Wait(); - } - - Device newDevice = manager.AddDeviceAsync(new Device(m_applicationName)).Result; - if (newDevice != null) - { - string hostname = ownerConnectionString.Substring(0, ownerConnectionString.IndexOf(";")); - string deviceConnectionString = hostname + ";DeviceId=" + m_applicationName + ";SharedAccessKey=" + newDevice.Authentication.SymmetricKey.PrimaryKey; - SecureIoTHubToken.Write(m_applicationName, deviceConnectionString); + Trace("Please specify an application name as argument!"); + return; } else { - Trace("Could not register ourselves with IoT Hub using owner connection string: " + ownerConnectionString); + Trace("Publisher is starting up..."); } - } - else - { - Trace("IoT Hub owner connection string not found, registration with IoT Hub abandoned."); - } - // try to read connection string from secure store and open IoTHub client - Trace("Attemping to read connection string from secure store with certificate name: " + m_applicationName); - string connectionString = SecureIoTHubToken.Read(m_applicationName); - if (!string.IsNullOrEmpty(connectionString)) - { - Trace("Attemping to configure publisher with connection string: " + connectionString); - m_deviceClient = DeviceClient.CreateFromConnectionString(connectionString, Microsoft.Azure.Devices.Client.TransportType.Mqtt); - m_deviceClient.RetryPolicy = RetryPolicyType.Exponential_Backoff_With_Jitter; - m_deviceClient.OpenAsync().Wait(); - } - else - { - Trace("Device connection string not found in secure store."); - } - - ModuleConfiguration moduleConfiguration = new ModuleConfiguration(m_applicationName); - m_configuration = moduleConfiguration.Configuration; - m_configuration.CertificateValidator.CertificateValidation += new CertificateValidationEventHandler(CertificateValidator_CertificateValidation); + m_applicationName = args[0]; + ModuleConfiguration moduleConfiguration = new ModuleConfiguration(m_applicationName); + m_configuration = moduleConfiguration.Configuration; + m_configuration.CertificateValidator.CertificateValidation += new CertificateValidationEventHandler(CertificateValidator_CertificateValidation); - // update log configuration, if available - if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("_GW_LOGP"))) - { - m_configuration.TraceConfiguration.OutputFilePath = Environment.GetEnvironmentVariable("_GW_LOGP"); - m_configuration.TraceConfiguration.ApplySettings(); - } - - // get a list of persisted endpoint URLs and create a session for each. - try - { - // check if we have an env variable specifying the published nodes path, otherwise use current directory - string publishedNodesFilePath = Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + "publishednodes.json"; - if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("_GW_PNFP"))) + // start our server interface + try { - publishedNodesFilePath = Environment.GetEnvironmentVariable("_GW_PNFP"); + Trace("Starting server on endpoint " + m_configuration.ServerConfiguration.BaseAddresses[0].ToString() + "..."); + m_server.Start(m_configuration); + Trace("Server started."); } - - Trace("Attemping to load nodes file from: " + publishedNodesFilePath); - m_nodesLookups = JsonConvert.DeserializeObject(File.ReadAllText(publishedNodesFilePath)); - Trace("Loaded " + m_nodesLookups.Count.ToString() + " nodes."); - } - catch (Exception ex) - { - Trace("Nodes file loading failed with: " + ex.Message); - } - - foreach (NodeLookup nodeLookup in m_nodesLookups) - { - if (!m_endpointUrls.Contains(nodeLookup.EndPointURL)) + catch (Exception ex) { - m_endpointUrls.Add(nodeLookup.EndPointURL); + Trace("Starting server failed with: " + ex.Message); } - } - // start the server - try - { - Trace("Starting server on endpoint " + m_configuration.ServerConfiguration.BaseAddresses[0].ToString() + "..."); - m_server.Start(m_configuration); - Trace("Server started."); - } - catch (Exception ex) - { - Trace("Starting server failed with: " + ex.Message); - } - - // connect to servers - Trace("Attemping to connect to servers..."); - try - { - List connectionAttempts = new List(); - foreach (Uri endpointUrl in m_endpointUrls) + // check if we also received an owner connection string + string ownerConnectionString = string.Empty; + if ((args.Length > 1) && !string.IsNullOrEmpty(args[1])) { - Trace("Connecting to server: " + endpointUrl); - connectionAttempts.Add(EndpointConnect(endpointUrl)); + ownerConnectionString = args[1]; + } + else + { + Trace("IoT Hub owner connection string not passed as argument."); + + // check if we have an environment variable to register ourselves with IoT Hub + if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("_HUB_CS"))) + { + ownerConnectionString = Environment.GetEnvironmentVariable("_HUB_CS"); + } } - // Wait for all sessions to be connected - Task.WaitAll(connectionAttempts.ToArray()); - } - catch (Exception ex) - { - Trace("Exception: " + ex.ToString() + "\r\n" + ex.InnerException != null ? ex.InnerException.ToString() : null); - } + // register ourselves with IoT Hub + if (ownerConnectionString != string.Empty) + { + Trace("Attemping to register ourselves with IoT Hub using owner connection string: " + ownerConnectionString); + RegistryManager manager = RegistryManager.CreateFromConnectionString(ownerConnectionString); + + // remove any existing device + Device existingDevice = manager.GetDeviceAsync(m_applicationName).Result; + if (existingDevice != null) + { + manager.RemoveDeviceAsync(m_applicationName).Wait(); + } + + Device newDevice = manager.AddDeviceAsync(new Device(m_applicationName)).Result; + if (newDevice != null) + { + string hostname = ownerConnectionString.Substring(0, ownerConnectionString.IndexOf(";")); + string deviceConnectionString = hostname + ";DeviceId=" + m_applicationName + ";SharedAccessKey=" + newDevice.Authentication.SymmetricKey.PrimaryKey; + SecureIoTHubToken.Write(m_applicationName, deviceConnectionString); + } + else + { + Trace("Could not register ourselves with IoT Hub using owner connection string: " + ownerConnectionString); + } + } + else + { + Trace("IoT Hub owner connection string not found, registration with IoT Hub abandoned."); + } + + // try to read connection string from secure store and open IoTHub client + Trace("Attemping to read connection string from secure store with certificate name: " + m_applicationName); + string connectionString = SecureIoTHubToken.Read(m_applicationName); + if (!string.IsNullOrEmpty(connectionString)) + { + Trace("Attemping to configure publisher with connection string: " + connectionString); + m_deviceClient = DeviceClient.CreateFromConnectionString(connectionString, Microsoft.Azure.Devices.Client.TransportType.Mqtt); + m_deviceClient.RetryPolicy = RetryPolicyType.Exponential_Backoff_With_Jitter; + m_deviceClient.OpenAsync().Wait(); + } + else + { + Trace("Device connection string not found in secure store."); + } + + // get a list of persisted endpoint URLs and create a session for each. + try + { + // check if we have an env variable specifying the published nodes path, otherwise use current directory + string publishedNodesFilePath = Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + "publishednodes.json"; + if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("_GW_PNFP"))) + { + publishedNodesFilePath = Environment.GetEnvironmentVariable("_GW_PNFP"); + } + + Trace("Attemping to load nodes file from: " + publishedNodesFilePath); + m_nodesLookups = JsonConvert.DeserializeObject(File.ReadAllText(publishedNodesFilePath)); + Trace("Loaded " + m_nodesLookups.Count.ToString() + " nodes."); + } + catch (Exception ex) + { + Trace("Nodes file loading failed with: " + ex.Message); + } - // subscribe to preconfigured nodes - Trace("Attemping to subscribe to published nodes..."); - if (m_nodesLookups != null) - { foreach (NodeLookup nodeLookup in m_nodesLookups) { - try + if (!m_endpointUrls.Contains(nodeLookup.EndPointURL)) { - CreateMonitoredItem(nodeLookup); - } - catch (Exception ex) - { - Trace("Unexpected error publishing node: " + ex.Message + "\r\nIgnoring node: " + nodeLookup.EndPointURL.AbsoluteUri + ", " + nodeLookup.NodeID.ToString()); + m_endpointUrls.Add(nodeLookup.EndPointURL); } } + + // connect to the other servers + Trace("Attemping to connect to servers..."); + try + { + List connectionAttempts = new List(); + foreach (Uri endpointUrl in m_endpointUrls) + { + Trace("Connecting to server: " + endpointUrl); + connectionAttempts.Add(EndpointConnect(endpointUrl)); + } + + // Wait for all sessions to be connected + Task.WaitAll(connectionAttempts.ToArray()); + } + catch (Exception ex) + { + Trace("Exception: " + ex.ToString() + "\r\n" + ex.InnerException != null ? ex.InnerException.ToString() : null); + } + + // subscribe to preconfigured nodes + Trace("Attemping to subscribe to published nodes..."); + if (m_nodesLookups != null) + { + foreach (NodeLookup nodeLookup in m_nodesLookups) + { + try + { + CreateMonitoredItem(nodeLookup); + } + catch (Exception ex) + { + Trace("Unexpected error publishing node: " + ex.Message + "\r\nIgnoring node: " + nodeLookup.EndPointURL.AbsoluteUri + ", " + nodeLookup.NodeID.ToString()); + } + } + } + + Trace("Publisher is running. Press enter to quit."); + Console.ReadLine(); + + foreach (Session session in m_sessions) + { + session.Close(); + } + + if (m_deviceClient != null) + { + m_deviceClient.CloseAsync().Wait(); + } } - - Console.WriteLine("Publisher is running. Press enter to quit."); - Console.ReadLine(); - - foreach (Session session in m_sessions) + catch (Exception e) { - session.Close(); - } - - if (m_deviceClient != null) - { - m_deviceClient.CloseAsync().Wait(); + Trace(e, "Unhandled exception in Publisher, exiting!"); } } From 2423d7f872ffb6393f0c11e9252478424a18e212 Mon Sep 17 00:00:00 2001 From: Erich Barnstedt Date: Thu, 13 Jul 2017 17:00:59 +0200 Subject: [PATCH 2/2] Loosen security configuration to be compatible with older OPC servers. --- src/ModuleConfiguration.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/ModuleConfiguration.cs b/src/ModuleConfiguration.cs index 842e04d..24bfbd0 100644 --- a/src/ModuleConfiguration.cs +++ b/src/ModuleConfiguration.cs @@ -177,6 +177,12 @@ namespace Opc.Ua.Publisher // the OperationTimeout should be twice the minimum value for PublishingInterval * KeepAliveCount, so set to 120s Configuration.TransportQuotas.OperationTimeout = 120000; + // allow SHA1 certificates for now as many OPC Servers still use them + Configuration.SecurityConfiguration.RejectSHA1SignedCertificates = false; + + // allow 1024 minimum key size as many OPC Servers still use them + Configuration.SecurityConfiguration.MinimumCertificateKeySize = 1024; + // validate the configuration now Configuration.Validate(Configuration.ApplicationType).Wait(); }