use of org.eclipse.milo.opcua.stack.core.types.enumerated.MessageSecurityMode in project milo by eclipse.
the class OpcServerHttpRequestHandler method channelRead0.
@Override
protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest httpRequest) throws Exception {
String host = httpRequest.headers().get(HttpHeaderNames.HOST);
String uri = httpRequest.uri();
String contentType = httpRequest.headers().get(HttpHeaderNames.CONTENT_TYPE);
String securityPolicyUri = httpRequest.headers().get("OPCUA-SecurityPolicy");
logger.debug("host={} uri={} contentType={} securityPolicy={}", host, uri, contentType, securityPolicyUri);
SecurityPolicy securityPolicy = securityPolicyUri != null ? SecurityPolicy.fromUri(securityPolicyUri) : SecurityPolicy.None;
MessageSecurityMode securityMode = securityPolicy == SecurityPolicy.None ? MessageSecurityMode.None : MessageSecurityMode.Sign;
EndpointDescription endpoint = stackServer.getEndpointDescriptions().stream().filter(e -> {
// TODO use contentType to determine which TransportProfile to match
boolean transportMatch = Objects.equals(e.getTransportProfileUri(), TransportProfile.HTTPS_UABINARY.getUri());
boolean pathMatch = Objects.equals(EndpointUtil.getPath(e.getEndpointUrl()), uri);
boolean securityPolicyMatch = Objects.equals(e.getSecurityPolicyUri(), securityPolicy.getUri());
boolean securityModeMatch = Objects.equals(e.getSecurityMode(), securityMode);
return transportMatch && pathMatch && securityPolicyMatch && securityModeMatch;
}).findFirst().orElseThrow(() -> new UaException(StatusCodes.Bad_TcpEndpointUrlInvalid, "unrecognized endpoint uri: " + uri));
ServerSecureChannel secureChannel = new ServerSecureChannel();
// TODO shared id per endpoint URL / path?
secureChannel.setChannelId(0L);
secureChannel.setSecurityPolicy(securityPolicy);
secureChannel.setMessageSecurityMode(securityMode);
ByteString thumbprint = ByteString.of(DigestUtil.sha1(endpoint.getServerCertificate().bytesOrEmpty()));
Optional<X509Certificate[]> certificateChain = stackServer.getConfig().getCertificateManager().getCertificateChain(thumbprint);
Optional<KeyPair> keyPair = stackServer.getConfig().getCertificateManager().getKeyPair(thumbprint);
certificateChain.ifPresent(chain -> {
secureChannel.setLocalCertificateChain(chain);
secureChannel.setLocalCertificate(chain[0]);
});
keyPair.ifPresent(secureChannel::setKeyPair);
OpcUaBinaryStreamDecoder decoder = new OpcUaBinaryStreamDecoder(stackServer.getSerializationContext());
decoder.setBuffer(httpRequest.content());
try {
UaRequestMessage request = (UaRequestMessage) decoder.readMessage(null);
UInteger requestHandle = request.getRequestHeader().getRequestHandle();
InetSocketAddress remoteSocketAddress = (InetSocketAddress) ctx.channel().remoteAddress();
ServiceRequest serviceRequest = new ServiceRequest(stackServer, request, endpoint, secureChannel.getChannelId(), remoteSocketAddress.getAddress(), null);
serviceRequest.getFuture().whenComplete((response, fault) -> {
if (response != null) {
sendServiceResponse(ctx, request, response);
} else {
sendServiceFault(ctx, requestHandle, fault);
}
});
stackServer.onServiceRequest(uri, serviceRequest);
} catch (Throwable t) {
logger.error("Error decoding UaRequestMessage", t);
sendServiceFault(ctx, null, t);
}
}
use of org.eclipse.milo.opcua.stack.core.types.enumerated.MessageSecurityMode in project vantiq-extension-sources by Vantiq.
the class OpcUaESClient method createClient.
@SuppressWarnings({ "PMD.CognitiveComplexity", "PMD.MethodLengthCheck" })
private OpcUaClient createClient(Map<String, Object> config) throws Exception {
if (storageDirectory == null) {
throw new OpcExtConfigException(ERROR_PREFIX + ".missingStorageDirectory: No storage directory specified.");
}
SecurityPolicy securityPolicy = determineSecurityPolicy(config);
MessageSecurityMode msgSecMode = determineMessageSecurityMode(config);
File securityDir = new File(storageDirectory, SECURITY_DIRECTORY);
if (!securityDir.exists() && !securityDir.mkdirs()) {
throw new OpcExtConfigException(ERROR_PREFIX + ".invalidStorageDirectory: unable to create security dir: " + securityDir);
}
log.info("security temp dir: {}", securityDir.getAbsolutePath());
keyStoreManager = new KeyStoreManager().load(securityDir);
IdentityProvider idProvider = constructIdentityProvider(config);
List<EndpointDescription> endpoints;
discoveryEndpoint = (String) config.get(OpcConstants.CONFIG_DISCOVERY_ENDPOINT);
serverEndpoint = (String) config.get(OpcConstants.CONFIG_SERVER_ENDPOINT);
if (discoveryEndpoint == null && serverEndpoint == null) {
String errorMsg = ERROR_PREFIX + ".noDiscoveryEndpoint: No discovery or server endpoint was provided in the configuration.";
log.error(errorMsg);
throw new OpcExtConfigException(errorMsg);
}
OpcUaClientConfig opcConfig;
try {
endpoints = DiscoveryClient.getEndpoints(discoveryEndpoint).get();
} catch (Throwable ex) {
try {
// try the explicit discovery endpoint as well
String discoveryUrl = discoveryEndpoint + "/discovery";
log.info("Trying explicit discovery URL: {}", discoveryUrl);
endpoints = DiscoveryClient.getEndpoints(discoveryUrl).get();
} catch (ExecutionException e) {
String errMsg = ERROR_PREFIX + ".discoveryError: Could not discover OPC Endpoints:" + e.getClass().getName() + "::" + e.getMessage();
log.error(ERROR_PREFIX + ".discoveryError: Could not discover OPC Endpoints: {} :: {}", e.getClass().getName(), e.getMessage());
throw new OpcExtConfigException(errMsg, e);
}
}
if (log.isDebugEnabled()) {
logDiscoveredEndpoints(endpoints);
}
List<EndpointDescription> validEndpoints = endpoints.stream().filter(e -> (e.getSecurityPolicyUri().equals(securityPolicy.getUri()) && e.getSecurityMode().equals(msgSecMode))).collect(Collectors.toList());
if (log.isDebugEnabled()) {
logAcceptableEndpoints(validEndpoints, securityPolicy, msgSecMode);
// The following code is here for testing only. It allows us to fake a poorly configured
// server that reports invalid or unreachable endpoints as part of discovery. This is, for
// reasons I'm sure i don't agree with, part of the protocol, so we must tolerate it. The
// purportedly proper response is to substitute the address used for discovery for any
// unreachable addresses. This, of course, makes little sense since the whole point of discovery
// is to allow these to be spread across different nodes. But I didn't write the spec.
Boolean fakeBadAddress = (Boolean) config.get(OpcConstants.CONFIG_TEST_DISCOVERY_UNREACHABLE);
if (fakeBadAddress != null && fakeBadAddress) {
List<EndpointDescription> newValidEndpoints = new ArrayList<>();
for (EndpointDescription e : validEndpoints) {
URI url = new URI(e.getEndpointUrl());
URI borkedUri = new URI(url.getScheme(), null, "utterlyWorthlessHostThatShouldNeverResolve", url.getPort(), url.getPath(), null, null);
EndpointDescription borkedEd = new EndpointDescription(borkedUri.toString(), e.getServer(), e.getServerCertificate(), e.getSecurityMode(), e.getSecurityPolicyUri(), e.getUserIdentityTokens(), e.getTransportProfileUri(), e.getSecurityLevel());
newValidEndpoints.add(borkedEd);
}
validEndpoints = newValidEndpoints;
}
}
// First, we'll look for an endpoint that doesn't contain localhost. This is, generally,
// a not too useful configuration since localhost is always a relative address.
EndpointDescription endpoint = validEndpoints.stream().filter(e -> {
try {
// Note: Must use URI here. If you use URL, it will fail with
// a MailformedURLException because the generic system doesn't
// understand opc.tcp: as a scheme/protocol.
URI url = new URI(e.getEndpointUrl());
InetAddress ina = InetAddress.getByName(url.getHost());
if (!ina.isLoopbackAddress() || ina.isReachable(3000)) {
return true;
}
} catch (UnknownHostException | URISyntaxException ex) {
log.warn("Recoverable error during discovered server URL validation:" + ex.getClass().getName() + "::" + ex.getMessage() + "-->" + e.getEndpointUrl());
} catch (Exception ex) {
// This means that we have some non-optimal addresses returned by discovery.
// In these cases, we'll leave it up to the SDK & network stack to figure out how to get there.
log.debug("Recoverable error during discovered server URL validation. Left to network stack to resolve:" + ex.getClass().getName() + "::" + ex.getMessage() + "-->" + e.getEndpointUrl());
}
return false;
}).findFirst().orElse(null);
if (endpoint == null) {
// Discovery server returned either no reasonable endpoints or none that weren't a loopback.
log.warn("No servers at reachable, non-loopback addresses found via discovery. " + "Fixing up addresses to match discovery server.");
endpoint = validEndpoints.stream().findFirst().orElse(null);
if (endpoint != null) {
endpoint = fixLookbackAddress(endpoint);
}
}
if (endpoint == null) {
throw new Exception("No acceptable endpoints returned for security policy: " + securityPolicy.getUri() + " and security mode " + msgSecMode);
}
if (serverEndpoint != null) {
// Then we'll override the endpoint provided but otherwise use the endpoint descriptor returned.
// The SDK seems to have an issue when no EndpointDescriptor is provided.
EndpointDescription newEndpoint = new EndpointDescription(serverEndpoint, endpoint.getServer(), endpoint.getServerCertificate(), endpoint.getSecurityMode(), endpoint.getSecurityPolicyUri(), endpoint.getUserIdentityTokens(), endpoint.getTransportProfileUri(), endpoint.getSecurityLevel());
log.debug("Replacing endpoint address with provided serverEndpoint: {} --> {}", endpoint.getEndpointUrl(), newEndpoint.getEndpointUrl());
endpoint = newEndpoint;
}
log.info("Using discovered endpoint: {} [{}, {}]", endpoint.getEndpointUrl(), securityPolicy, msgSecMode.toString());
opcConfig = OpcUaClientConfig.builder().setApplicationName(LocalizedText.english("VANTIQ OPC-UA Source")).setApplicationUri("urn:io:vantiq:extsrc:opcua:client").setCertificate(keyStoreManager.getClientCertificate()).setKeyPair(keyStoreManager.getClientKeyPair()).setEndpoint(endpoint).setIdentityProvider(idProvider).setRequestTimeout(uint(5000)).build();
return OpcUaClient.create(opcConfig);
}
use of org.eclipse.milo.opcua.stack.core.types.enumerated.MessageSecurityMode in project vantiq-extension-sources by Vantiq.
the class OpcUaESClient method determineMessageSecurityMode.
private MessageSecurityMode determineMessageSecurityMode(Map<String, Object> config) throws OpcExtConfigException {
// config.messageSecurityMode should be the URI for the appropriate security policy.
String msgSecModeSpec = (String) config.get(OpcConstants.CONFIG_MESSAGE_SECURITY_MODE);
MessageSecurityMode msgSecMode;
if (msgSecModeSpec == null || msgSecModeSpec.isEmpty()) {
// No message security mode will default to either #NONE or #SignAndEncrypt, depending on the security policy. We will, however, log a warning\
SecurityPolicy secPol = determineSecurityPolicy(config);
if (secPol.equals(SecurityPolicy.None)) {
msgSecModeSpec = MessageSecurityMode.None.toString();
} else {
msgSecModeSpec = MessageSecurityMode.SignAndEncrypt.toString();
}
log.warn(ERROR_PREFIX + ".defaultMessageSecurityMode: No OPC UA message security mode was specified in the configuration. " + "Using default value of '{}' based on the securityPolicy value of '{}'", msgSecModeSpec, secPol.getUri());
}
try {
msgSecMode = MessageSecurityMode.valueOf(msgSecModeSpec);
return msgSecMode;
} catch (IllegalArgumentException e) {
String errMsg = ERROR_PREFIX + ".invalidMessageSecurityMode: " + msgSecModeSpec + " is not a valid message security mode.";
log.error(errMsg);
throw new OpcExtConfigException(errMsg, e);
}
}
use of org.eclipse.milo.opcua.stack.core.types.enumerated.MessageSecurityMode in project vantiq-extension-sources by Vantiq.
the class Connection method testConnectionSecureUpw.
@Test
public void testConnectionSecureUpw() throws Exception {
List<EndpointDescription> eps = exampleServer.getServer().getEndpointDescriptions();
EnumSet<MessageSecurityMode> serverMsgModes = EnumSet.noneOf(MessageSecurityMode.class);
EnumSet<SecurityPolicy> serverSecPols = EnumSet.noneOf(SecurityPolicy.class);
for (EndpointDescription ep : eps) {
if (ep.getEndpointUrl().startsWith("opc.tpc")) {
// At present, these are all we test
serverSecPols.add(SecurityPolicy.fromUri(ep.getSecurityPolicyUri()));
serverMsgModes.add(ep.getSecurityMode());
}
}
log.debug("For example server found secPols: {}, msgSec: {}", serverSecPols, serverMsgModes);
// Below, we'll traverse the valid combinations. None's must be paired and are tested elsewhere
for (SecurityPolicy secPol : serverSecPols) {
if (!secPol.equals(SecurityPolicy.None)) {
for (MessageSecurityMode msgSec : serverMsgModes) {
if (!msgSec.equals(MessageSecurityMode.None)) {
log.info("Attempting sync connection using [{}, {}]", secPol, msgSec);
makeConnection(false, secPol.getUri(), msgSec.toString(), true);
log.info("Attempting sync connection using [{}, {}]", secPol, "(missing)");
makeConnection(false, secPol.getUri(), // Also check that the defaulting works correctly
null, true);
log.info("Attempting async connection using [{}, {}]", secPol, msgSec);
makeConnection(true, secPol.getUri(), msgSec.toString(), true);
log.info("Attempting async connection using [{}, {}] with explicit anonymous user", secPol, msgSec);
makeConnection(true, secPol.getUri(), msgSec.toString(), OpcConstants.CONFIG_IDENTITY_ANONYMOUS, null, true);
// Valid user/pw combos from ExampleServer:
// "user, password1" & "admin, password2"
String[] upwCombos = { "user, password1", "admin,password2", "user, password1" };
for (String uPw : upwCombos) {
log.info("Attempting sync connection using [{}, {}] using username/password: '{}'", secPol, msgSec, uPw);
makeConnection(false, secPol.getUri(), msgSec.toString(), OpcConstants.CONFIG_IDENTITY_USERNAME_PASSWORD, uPw, true);
}
for (String uPw : upwCombos) {
log.info("Attempting async connection using [{}, {}] using username/password: '{}'", secPol, msgSec, uPw);
makeConnection(true, secPol.getUri(), msgSec.toString(), OpcConstants.CONFIG_IDENTITY_USERNAME_PASSWORD, uPw, true);
}
}
}
}
}
}
use of org.eclipse.milo.opcua.stack.core.types.enumerated.MessageSecurityMode in project vantiq-extension-sources by Vantiq.
the class Connection method testConnectionSecureCert.
@Test
public void testConnectionSecureCert() throws Exception {
List<EndpointDescription> eps = exampleServer.getServer().getEndpointDescriptions();
EnumSet<MessageSecurityMode> serverMsgModes = EnumSet.noneOf(MessageSecurityMode.class);
EnumSet<SecurityPolicy> serverSecPols = EnumSet.noneOf(SecurityPolicy.class);
for (EndpointDescription ep : eps) {
if (ep.getEndpointUrl().startsWith("opc.tpc")) {
// At present, these are all we test
serverSecPols.add(SecurityPolicy.fromUri(ep.getSecurityPolicyUri()));
serverMsgModes.add(ep.getSecurityMode());
}
}
// Below, we'll traverse the valid combinations. None's must be paired and are tested elsewhere
for (SecurityPolicy secPol : serverSecPols) {
if (!secPol.equals(SecurityPolicy.None)) {
for (MessageSecurityMode msgSec : serverMsgModes) {
if (!msgSec.equals(MessageSecurityMode.None)) {
// Defaults tested in *Upw test...
for (String certKey : trustedTestCerts) {
log.info("Attempting sync connection using [{}, {}] using certificate: '{}'", secPol, msgSec, certKey);
makeConnection(false, secPol.getUri(), msgSec.toString(), OpcConstants.CONFIG_IDENTITY_CERTIFICATE, certKey, true);
log.info("Attempting async connection using [{}, {}] using certificate: '{}'", secPol, msgSec, certKey);
makeConnection(true, secPol.getUri(), msgSec.toString(), OpcConstants.CONFIG_IDENTITY_CERTIFICATE, certKey, true);
}
}
}
}
}
}
Aggregations