Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Verify DiscoveryURL from OPC Server is resolvable #570

Merged
merged 18 commits into from Apr 11, 2023

Conversation

harrison-tin
Copy link
Collaborator

What this PR does / why we need it:
This PR solves the issue when OPC server returns a discoveryURL that contains a hostname instead of an IP address. If the broker is not able to resolve the hostname, then it will return an error. The suggested solution is to have the discovery handler try resolving the discoveryURL (by to_socket_addr), if it fails, then we will use the IP address that user passes in instead. Note it should only perform on Server type not DiscoveryServer type.
closes #555

Sample: Using Prosys OPC UA Simulation Server
Before:

harrison@harrison-Virtual-Machine:~/akri-opcua-test$ kubectl logs harrison-virtual-machine-akri-opcua-monitoring-2b9293-pod
.NET Core OPC UA Console Client Start
1 - Create an Application Configuration.
Exception System.IO.DirectoryNotFoundException: Could not find a part of the path '/etc/opcua-certs/client-pki/own/certs'.
   at System.IO.Enumeration.FileSystemEnumerator`1.CreateDirectoryHandle(String path, Boolean ignoreNotFound)
   at System.IO.Enumeration.FileSystemEnumerator`1.Init()
   at System.IO.Enumeration.FileSystemEnumerator`1..ctor(String directory, Boolean isNormalized, EnumerationOptions options)
   at System.IO.Enumeration.FileSystemEnumerable`1..ctor(String directory, FindTransform transform, EnumerationOptions options, Boolean isNormalized)
   at System.IO.Enumeration.FileSystemEnumerableFactory.FileInfos(String directory, String expression, EnumerationOptions options, Boolean isNormalized)
   at System.IO.DirectoryInfo.InternalEnumerateInfos(String path, String searchPattern, SearchTarget searchTarget, EnumerationOptions options)
   at System.IO.DirectoryInfo.GetFiles(String searchPattern, EnumerationOptions enumerationOptions)
   at System.IO.DirectoryInfo.GetFiles(String searchPattern)
   at OpcuaNodeMonitoring.MonitoringClient.GetCertificate(String certificateStorePath) in /src/Program.cs:line 172
   at OpcuaNodeMonitoring.MonitoringClient.CreateApplicationConfiguration() in /src/Program.cs:line 195 thrown when trying to use application certificate mounted at /etc/opcua-certs/client-pki/own/certs/. Using no security.
Application certificates not mounted, using unsecure connection with Security Policy None
Client is using a certificate with subject 
2 - Discover endpoints of opc.tcp://<redacted DESKTOP-HOSTNAME>:53530/OPCUA/SimulationServer.
info: Microsoft.Hosting.Lifetime[0]
      Now listening on: http://[::]:8083
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Production
info: Microsoft.Hosting.Lifetime[0]
      Content root path: /app
Exception: One or more errors occurred. (Error establishing a connection: BadNotConnected)
info: Microsoft.Hosting.Lifetime[0]
      Application is shutting down...

After:

.NET Core OPC UA Console Client Start
1 - Create an Application Configuration.
Exception System.IO.DirectoryNotFoundException: Could not find a part of the path '/etc/opcua-certs/client-pki/own/certs'.
   at System.IO.Enumeration.FileSystemEnumerator`1.CreateDirectoryHandle(String path, Boolean ignoreNotFound)
   at System.IO.Enumeration.FileSystemEnumerator`1.Init()
   at System.IO.Enumeration.FileSystemEnumerator`1..ctor(String directory, Boolean isNormalized, EnumerationOptions options)
   at System.IO.Enumeration.FileSystemEnumerable`1..ctor(String directory, FindTransform transform, EnumerationOptions options, Boolean isNormalized)
   at System.IO.Enumeration.FileSystemEnumerableFactory.FileInfos(String directory, String expression, EnumerationOptions options, Boolean isNormalized)
   at System.IO.DirectoryInfo.InternalEnumerateInfos(String path, String searchPattern, SearchTarget searchTarget, EnumerationOptions options)
   at System.IO.DirectoryInfo.GetFiles(String searchPattern, EnumerationOptions enumerationOptions)
   at System.IO.DirectoryInfo.GetFiles(String searchPattern)
   at OpcuaNodeMonitoring.MonitoringClient.GetCertificate(String certificateStorePath) in /src/Program.cs:line 172
   at OpcuaNodeMonitoring.MonitoringClient.CreateApplicationConfiguration() in /src/Program.cs:line 195 thrown when trying to use application certificate mounted at /etc/opcua-certs/client-pki/own/certs/. Using no security.
Application certificates not mounted, using unsecure connection with Security Policy None
Client is using a certificate with subject 
2 - Discover endpoints of opc.tcp://<redacted IP address>:53530/OPCUA/SimulationServer.
info: Microsoft.Hosting.Lifetime[0]
      Now listening on: http://[::]:8083
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Production
info: Microsoft.Hosting.Lifetime[0]
      Content root path: /app
    Selected endpoint uses: None
3 - Create a session with OPC UA server.
4 - Browse the OPC UA server namespace.
.
.
.

Special notes for your reviewer:

If applicable:

  • this PR has an associated PR with documentation in akri-docs
  • this PR contains unit tests
  • added code adheres to standard Rust formatting (cargo fmt)
  • code builds properly (cargo build)
  • code is free of common mistakes (cargo clippy)
  • all Akri tests succeed (cargo test)
  • inline documentation builds (cargo doc)
  • all commits pass the DCO bot check by being signed off -- see the failing DCO check for instructions on how to retroactively sign commits

Signed-off-by: harrisontin <harrisontin@microsoft.com>
Signed-off-by: harrisontin <harrisontin@microsoft.com>
Signed-off-by: harrisontin <harrisontin@microsoft.com>
Signed-off-by: harrisontin <harrisontin@microsoft.com>
@harrison-tin harrison-tin changed the title Opc ip Verify DiscoveryURL from OPC Server is resolvable Mar 21, 2023
Signed-off-by: harrisontin <harrisontin@microsoft.com>
Signed-off-by: harrisontin <harrisontin@microsoft.com>
@harrison-tin
Copy link
Collaborator Author

/version patch

@github-actions github-actions bot added the version/patch Patch version change is needed label Mar 21, 2023
github-actions bot and others added 4 commits March 21, 2023 18:44
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Signed-off-by: harrisontin <harrisontin@microsoft.com>
Copy link
Contributor

@diconico07 diconico07 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not familiar with OPC UA URLs so I handled this with "generic" URLs in mind, so this functionally looks good to me.

I added some comments about error handling/safety, as I don't think it is likeable that a rogue/faulty discovered device can make the discovery handler panic (this might not be enough as I didn't check the existing code path, but this should make it less likely).

// Test that it converts the discovery url to an ip address if the discovery url is a hostname that is not resolvable
fn test_get_discovery_url_ip() {
let ip_url = "opc.tcp://192.168.0.1:50000/";
let discovery_url = "opc.tcp://OPCTest:50000/OPCUA/Simluation";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From a DNS point of view this could one day become resolvable (unlikely but yet), we may want to use the reserved tld .invalid here to ensure this (and make it clearer at first glance this is a not resolvable domain).

Suggested change
let discovery_url = "opc.tcp://OPCTest:50000/OPCUA/Simluation";
let discovery_url = "opc.tcp://OPCTest.invalid:50000/OPCUA/Simluation";

Comment on lines 188 to 190
let url = Url::parse(&discovery_url).unwrap();
let mut path = url.path().to_string();
let host = url.host_str().unwrap();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not really fond of panicking if an external service sends crappy things, I think it is better to directly return an Option<String> here and return None if the URL we got is completely invalid (and log an error so the user can be aware of that).

let url = Url::parse(&discovery_url).unwrap();
let mut path = url.path().to_string();
let host = url.host_str().unwrap();
let port = url.port().unwrap();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather use unwrap_or here with the default port, as it is valid to omit the port in a URL

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a standard default port for opcua servers?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, there is one defined in the opcua crate (didn't check the specs though): https://docs.rs/opcua/latest/opcua/core/constants/constant.DEFAULT_OPC_UA_SERVER_PORT.html

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And IANA confirm that same port number (4840) for "OPC UA Connection Protocol"

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thank you! updated

Signed-off-by: harrisontin <harrisontin@microsoft.com>
Signed-off-by: harrisontin <harrisontin@microsoft.com>
Signed-off-by: harrisontin <harrisontin@microsoft.com>
Signed-off-by: harrisontin <harrisontin@microsoft.com>
Copy link
Contributor

@kate-goldenring kate-goldenring left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

discovery-handlers/opcua/src/discovery_impl.rs Outdated Show resolved Hide resolved
Signed-off-by: harrisontin <harrisontin@microsoft.com>
Signed-off-by: harrisontin <harrisontin@microsoft.com>
Signed-off-by: harrisontin <harrisontin@microsoft.com>
@yujinkim-msft yujinkim-msft merged commit 7d043e6 into project-akri:main Apr 11, 2023
51 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
version/patch Patch version change is needed
Projects
None yet
5 participants