Search code examples
opc-uanode-opcua

Node-OPCUA Connect to Server with unknown securityMode/securityPolicy


I am trying to connect to an opcua server with unknown securityMode and securityPolicy. Probably I have a basic understanding problem, but according to the OPCUA specification I can get the EndpointDescription via the local DiscoveryServer and then open a SecureChannel (session).

Simple Discovery Process

Currently I connect to the server without specifying the security settings, read out the endpoints and would then select an appropriate security setting and reconnect.

const getEndpoints = function (endpointUrl) {
  return new Promise(function (resolve, reject) {
    let client = new opcua.OPCUAClient();
    client.connect(endpointUrl, function (err) {

      if(err) reject(new Error(err));
      client.getEndpointsRequest(function (err,endpoints) {
        let reducedEndpoints = endpoints.map(endpoint => 
          ({ 
            endpointUrl: endpoint.endpointUrl, 
            securityMode: endpoint.securityMode, 
            securityPolicy: endpoint.securityPolicyUri,
          })
        );
        resolve(endpoints);
        // resolve(reducedEndpoints);
        client.disconnect();
      })
    })
  })
}

const connect = function (endpointUrl, options) {
  return new Promise(function (resolve, reject) {
    const defaultOptions = {
      connectionStrategy: {
        maxRetry: 6,
      },
      keepSessionAlive: true,
      endpoint_must_exist: false,
      securityMode: options.MessageSecurityMode.NONE,
      securityPolicy: SecurityPolicy.None,
    };

    let client = new opcua.OPCUAClient(Object.assign({}, defaultOptions, options));

    client.connect(endpointUrl, function (err) {
      if(err) {
        reject(new Error(err));
      }
      resolve(client)
    });
  });
};

That doesn't feel right. It would be nice if someone would help me with an example.

Best Regards


Solution

    • A client usually queries the endpoints of a OPCUA server in order to find out what will be the best security and encryption mode it will use to connect to the server.
    • getEndpoints is one of the service that doesn't require the client to open a session on the Server.
        // with [email protected]
        const opcua = require("node-opcua");
        async function getEndpoints(endpointUrl) {
            let client = new opcua.OPCUAClient();
            await client.connect(endpointUrl);
            const endpoints =  await client.getEndpoints();
            const reducedEndpoints = endpoints.map(endpoint => ({ 
                endpointUrl: endpoint.endpointUrl, 
                securityMode: endpoint.securityMode.toString(), 
                securityPolicy: endpoint.securityPolicyUri.toString(),
            }));
            await client.disconnect();
            return reducedEndpoints;
        }
    
        async function main() {
          const endpoints = await getEndpoints("opc.tcp://opcuademo.sterfive.com:26543");
            console.log(endpoints);
        }
        main().then();
    

    This code will output:

      [ { endpointUrl: 'opc.tcp://opcuademo.sterfive.com:26543',
          securityMode: 'NONE',
          securityPolicy: 'http://opcfoundation.org/UA/SecurityPolicy#None' },
        { endpointUrl: 'opc.tcp://opcuademo.sterfive.com:26543',
          securityMode: 'SIGN',
          securityPolicy: 'http://opcfoundation.org/UA/SecurityPolicy#Basic128Rsa15' },
        { endpointUrl: 'opc.tcp://opcuademo.sterfive.com:26543',
          securityMode: 'SIGN',
          securityPolicy: 'http://opcfoundation.org/UA/SecurityPolicy#Basic256' },
        { endpointUrl: 'opc.tcp://opcuademo.sterfive.com:26543',
          securityMode: 'SIGN',
          securityPolicy: 'http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256' },
        { endpointUrl: 'opc.tcp://opcuademo.sterfive.com:26543',
          securityMode: 'SIGNANDENCRYPT',
          securityPolicy: 'http://opcfoundation.org/UA/SecurityPolicy#Basic128Rsa15' },
        { endpointUrl: 'opc.tcp://opcuademo.sterfive.com:26543',
          securityMode: 'SIGNANDENCRYPT',
          securityPolicy: 'http://opcfoundation.org/UA/SecurityPolicy#Basic256' },
        { endpointUrl: 'opc.tcp://opcuademo.sterfive.com:26543',
          securityMode: 'SIGNANDENCRYPT',
          securityPolicy: 'http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256' } ]
    

    This being said, an node-opcua client will automatically query the server endpoint during connect and verifies that the securityMode and securityPolicy requested by the user is available.

      // with [email protected]
      const opcua = require("node-opcua");
      async function verifyEndpointAndConnect(endpointUrl) {
          let client = new opcua.OPCUAClient();
          await client.connect(endpointUrl);
    
          // note that client has already requested the server endpoints
          // during the connection. We can now simply query the Application
          // description matching our security settings
          const applicationDescription = client.findEndpointForSecurity(
              opcua.MessageSecurityMode.SIGN,
              opcua.SecurityPolicy.Basic256Sha256
          );
    
          await client.disconnect();
    
          if (applicationDescription) {
               console.log("Yes! the server support this endpoints:");
               console.log(applicationDescription.toString());
          }else {
               console.log("Sorry! this server do not support the requested security mode");
              return;
          }      
    
          // let recreate our client with the requested security mode
          client = new opcua.OPCUAClient({
              securityMode: opcua.MessageSecurityMode.SIGN,
              securityPolicy: opcua.SecurityPolicy.Basic256Sha256,
          });
    
          await client.connect(endpointUrl);
          // [...] do something with this connected client.
          await client.disconnect();
    
      }
    
      async function main() {
        await verifyEndpointAndConnect("opc.tcp://opcuademo.sterfive.com:26543");
        console.log("done");
      }
      main();