Skip to content

Commit

Permalink
Documentation updates and related code alignments.
Browse files Browse the repository at this point in the history
Signed-off-by: Łukasz Dywicki <[email protected]>
  • Loading branch information
splatch committed Dec 20, 2023
1 parent 73b76bf commit a504f77
Show file tree
Hide file tree
Showing 7 changed files with 87 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,19 @@

public class Limits implements Configuration {

private static final int DEFAULT_MAX_CHUNK_COUNT = 64;
private static final int DEFAULT_MAX_MESSAGE_SIZE = 2097152;
private static final int DEFAULT_RECEIVE_BUFFER_SIZE = 65535;
private static final int DEFAULT_SEND_BUFFER_SIZE = 65535;
private static final int DEFAULT_MAX_MESSAGE_SIZE = 2097152;
private static final int DEFAULT_MAX_CHUNK_COUNT = 64;

public static final Limits DEFAULT = new Limits(
DEFAULT_RECEIVE_BUFFER_SIZE,
DEFAULT_SEND_BUFFER_SIZE,
DEFAULT_MAX_MESSAGE_SIZE,
DEFAULT_MAX_CHUNK_COUNT
);

private final int receiveBufferSize;
private final int sendBufferSize;
private final int maxMessageSize;
private final int maxChunkCount;
@ConfigurationParameter("receiveBufferSize")
private int receiveBufferSize;
@ConfigurationParameter("sendBufferSize")
private int sendBufferSize;
@ConfigurationParameter("maxMessageSize")
private int maxMessageSize;
@ConfigurationParameter("maxChunkCount")
private int maxChunkCount;

public Limits() {
this(DEFAULT_RECEIVE_BUFFER_SIZE, DEFAULT_SEND_BUFFER_SIZE, DEFAULT_MAX_MESSAGE_SIZE, DEFAULT_MAX_CHUNK_COUNT);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,6 @@
package org.apache.plc4x.java.opcua.config;

import java.security.cert.X509Certificate;
import java.time.Duration;
import org.apache.plc4x.java.opcua.context.SecureChannel;
import org.apache.plc4x.java.opcua.readwrite.PascalByteString;
import org.apache.plc4x.java.opcua.security.MessageSecurity;
import org.apache.plc4x.java.opcua.security.SecurityPolicy;
import org.apache.plc4x.java.spi.configuration.Configuration;
Expand All @@ -31,11 +28,12 @@

public class OpcuaConfiguration implements Configuration {

public static final long DEFAULT_CONNECTION_LIFETIME = 36000000;
public static final long DEFAULT_CHANNEL_LIFETIME = 3600000;

public static final long DEFAULT_REQUEST_TIMEOUT = 5 * 60 * 1000L;
public static final long DEFAULT_SESSION_TIMEOUT = 120000;
public static final long DEFAULT_NEGOTIATION_TIMEOUT = 60000;

public static final long DEFAULT_SESSION_TIMEOUT = 120000L;
public static final long DEFAULT_REQUEST_TIMEOUT = 30000;

@ConfigurationParameter("protocolCode")
private String protocolCode;
Expand Down Expand Up @@ -73,16 +71,16 @@ public class OpcuaConfiguration implements Configuration {
private X509Certificate serverCertificate;

@ConfigurationParameter("channelLifetime")
private long channelLifetime = DEFAULT_CONNECTION_LIFETIME;

@ConfigurationParameter("requestTimeout")
private long requestTimeout = DEFAULT_REQUEST_TIMEOUT;
private long channelLifetime = DEFAULT_CHANNEL_LIFETIME;

@ConfigurationParameter("sessionTimeout")
private long sessionTimeout = DEFAULT_SESSION_TIMEOUT;

@ConfigurationParameter("openChannelTimeout")
private long openChannelTimeout = DEFAULT_REQUEST_TIMEOUT;
@ConfigurationParameter("negotiationTimeout")
private long negotiationTimeout = DEFAULT_NEGOTIATION_TIMEOUT;

@ConfigurationParameter("requestTimeout")
private long requestTimeout = DEFAULT_REQUEST_TIMEOUT;

@ComplexConfigurationParameter(prefix = "encoding", defaultOverrides = {}, requiredOverrides = {})
private Limits limits = new Limits();
Expand Down Expand Up @@ -147,12 +145,16 @@ public long getChannelLifetime() {
return channelLifetime;
}

public long getSessionTimeout() {
return sessionTimeout;
}

public long getRequestTimeout() {
return requestTimeout;
}

public long getOpenChannelTimeout() {
return openChannelTimeout;
public long getNegotiationTimeout() {
return negotiationTimeout;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ public CompletableFuture<OpcuaAcknowledgeResponse> requestHello() {
//CompletableFuture<OpcuaAcknowledgeResponse> future = new CompletableFuture<>();

CompletableFuture<OpcuaAcknowledgeResponse> future = new CompletableFuture<>();
sendRequest(request, future, configuration.getOpenChannelTimeout())
sendRequest(request, future, configuration.getNegotiationTimeout())
.unwrap(OpcuaAPU::getMessage)
.check(OpcuaAcknowledgeResponse.class::isInstance)
.unwrap(OpcuaAcknowledgeResponse.class::cast)
Expand Down Expand Up @@ -239,7 +239,7 @@ private <T extends MessagePDU, R extends MessagePDU> CompletableFuture<R> reques
for (int count = chunks.size(), index = 0; index < count; index++) {
boolean last = index + 1 == count;
if (last) {
sendRequest(chunks.get(index), future, configuration.getRequestTimeout())
sendRequest(chunks.get(index), future, configuration.getNegotiationTimeout())
.unwrap(OpcuaAPU::getMessage)
.check(replyType::isInstance)
.unwrap(replyType::cast)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;
Expand Down Expand Up @@ -89,7 +88,7 @@ public class SecureChannel {
private final RequestTransactionManager tm;
private final OpcuaConfiguration configuration;
private final OpcuaDriverContext driverContext;
private Conversation conversation;
private final Conversation conversation;
private ScheduledFuture<?> keepAlive;
private final List<String> endpoints = new ArrayList<>();
private double sessionTimeout;
Expand All @@ -101,6 +100,7 @@ public SecureChannel(Conversation conversation, RequestTransactionManager tm, Op
this.configuration = configuration;
this.driverContext = driverContext;
this.endpoint = new PascalString(driverContext.getEndpoint());
this.sessionTimeout = configuration.getSessionTimeout();
if (authentication != null) {
if (authentication instanceof PlcUsernamePasswordAuthentication) {
this.username = ((PlcUsernamePasswordAuthentication) authentication).getUsername();
Expand Down Expand Up @@ -155,7 +155,7 @@ public CompletableFuture<ActivateSessionResponse> onConnect() {
public CompletableFuture<OpenSecureChannelResponse> onConnectOpenSecureChannel(SecurityTokenRequestType securityTokenRequestType) {
LOGGER.debug("Sending open secure channel message to {}", this.driverContext.getEndpoint());

RequestHeader requestHeader = conversation.createRequestHeader(configuration.getOpenChannelTimeout(), 0);
RequestHeader requestHeader = conversation.createRequestHeader(configuration.getNegotiationTimeout(), 0);

OpenSecureChannelRequest openSecureChannelRequest;
if (conversation.getSecurityPolicy() != SecurityPolicy.NONE) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@

package org.eclipse.milo.examples.server;

import static com.google.common.collect.Lists.newArrayList;
import static org.eclipse.milo.opcua.sdk.server.api.config.OpcUaServerConfig.USER_TOKEN_POLICY_ANONYMOUS;
import static org.eclipse.milo.opcua.sdk.server.api.config.OpcUaServerConfig.USER_TOKEN_POLICY_USERNAME;
import static org.eclipse.milo.opcua.sdk.server.api.config.OpcUaServerConfig.USER_TOKEN_POLICY_X509;
Expand Down Expand Up @@ -165,7 +164,7 @@ public TestMiloServer() throws Exception {
private Set<EndpointConfiguration> createEndpointConfigurations(X509Certificate certificate) {
Set<EndpointConfiguration> endpointConfigurations = new LinkedHashSet<>();

List<String> bindAddresses = newArrayList();
List<String> bindAddresses = new ArrayList<>();
bindAddresses.add("0.0.0.0");

Set<String> hostnames = new LinkedHashSet<>();
Expand Down
57 changes: 55 additions & 2 deletions src/site/asciidoc/users/protocols/opc-ua.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,35 @@ will propagate over an '<address>/discovery' endpoint. The most common issue her
configured and propagate the wrong external IP or URL address. If that is the case you can disable the discovery by
configuring it with a `false` value.

|| `username` | A username to authenticate to the OPCUA server with.
|| `password` | A password to authenticate to the OPCUA server with. |
The discovery phase is always conducted using `NONE` security policy.

| `securityPolicy` | `NONE` | The security policy applied to communication channel between driver and OPC UA server.
Default value assumes. Possible options are `NONE`, `Basic128Rsa15`, `Basic256`, `Basic256Sha256`, `Aes128_Sha256_RsaOaep`, `Aes256_Sha256_RsaPss`.
| `messageSecurity` | `SIGN_ENCRYPT` | The security policy applied to messages exchanged after handshake phase.
Each handshake uses `SIGN_ENCRYPT` mode to securely negotiate encryption keys used later for symmetric encryption.
In case when security policy defines secure value (i.e. `Basic256`) it will automatically impose full security on the message exchanges.

| `certDirectory` | | Filesystem location where security related files are expected to be found.
| `keyStoreFile` | | The Keystore file used to lookup client certificate and its private key.
Please note that in case if no value is provided driver will generate self-signed certificate for negotiation phase.
The keystore file is looked up under `$certDirectory/security/` directory.
Expected keystore type is PKCS12 (default since Java 11).
| `keyStorePassword` | | Java keystore password used to access keystore and private key.

| `username` | | A username to authenticate to the OPCUA server with.
| `password` | | A password to authenticate to the OPCUA server with.

3+| TCP encoding options
| `encoding.receiveBufferSize` | 65535 | Maximum size of received TCP transport message chunk value in bytes.
| `encoding.sendBufferSize` | 65535 | Maximum size of sent transport message chunk.
| `encoding.maxMessageSize` | 2097152 | Maximum size of complete message.
| `encoding.maxChunkCount` | 64 | Maximum number of chunks for both sent and received messages.

3+| Timeout options
| `channelLifetime` | `3600000` | Time for which negotiated secure channel, its keys and session remains open. Value in milliseconds, by default 60 minutes.
| `sessionTimeout` | `120000` | Expiry time for opened secure session, value in milliseconds. Defaults to 2 minutes.
| `negotiationTimeout` | `60000` | Timeout for all negotiation steps prior acceptance of application level operations - this timeout applies to open secure channel, create session and close calls. Defaults to 60 seconds.
| `requestTimeout` | `30000` | Timeout for read/write/subscribe calls. Value in milliseconds.

|===

Expand All @@ -89,6 +116,32 @@ opcua:tcp://127.0.0.1:12686?discovery=true&username=admin&password=password

Note the transport, port and options fields are optional.

=== Secure communication
The secure channel implementation within Apache PLC4X project have been tested against existing open source server implementations.
This includes Eclipse Milo (all modes) as well as OPC Foundation .NET server (except `Basic128Rsa15`).
Manual tests proven that driver is able to communicate with OPC UA server launched on PLCs as well as commercial simulators.

Depending on actual configuration of remote end there might be necessity to prepare client certificate.
Preparation of certificate is beyond driver, however in case when no client certificate is provided, it will be auto-generated to establish a session.

The security modes differ between themselves by strength of applied signature and encryption algorithms.
Driver is able to communicate with single security mode at the time.
Additionally, to security policy it is possible to specify `messageSecurity` option which indicates expected security settings after initial handshake.
By default, this option is set to `SIGN_ENCRYPT` which imposes high security settings and full encryption of exchanged message payloads.
In case when additional diagnostics is needed payloads has to be traced through TRACE level log entries.
The `SIGN` mode gives possibility o browse packets in tools such wireshark.

==== Negotiation procedure
Depending on settings driver might or might not attempt to discover endpoints from remote server.
In case when `discovery` option is set to `true` driver will look up server certificate through connection attempt.
The discovery option also enables checks of server endpoints for matching security settings.

Once initial discovery is completed and driver finds endpoint matching its security settings it will launch second connection attempt.
This attempt will propagate encoding limits described in table above.
Role of these options is declaration of values accepted and expected by client.
Once server returns its limits driver picks minimums of all these.
The only one note is that driver takes minimum of local receive and remote send buffer size.
It does same with local send and remote receive buffer.

=== Address Format
To read, write and subscribe to data, the OPC UA driver uses the variable declaration string of the OPC UA server it is
Expand Down

0 comments on commit a504f77

Please sign in to comment.