Skip to content

Commit

Permalink
Merge branch 'master' into tag-processor-metadata-exposing
Browse files Browse the repository at this point in the history
  • Loading branch information
FreiPaul authored Feb 13, 2025
2 parents 89aa02a + 587507b commit 4a0c2d5
Show file tree
Hide file tree
Showing 9 changed files with 384 additions and 139 deletions.
22 changes: 14 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,9 @@ input:
nodeIDs: ['ns=2;s=IoTSensors']
username: 'your-username' # optional (default: unset)
password: 'your-password' # optional (default: unset)
insecure: false | true # DEPRECATED, see below
securityMode: None | Sign | SignAndEncrypt # optional (default: unset)
securityPolicy: None | Basic256Sha256 # optional (default: unset)
securityPolicy: None | Basic128Rsa15 | Basic256 | Basic256Sha256 # optional (default: unset)
clientCertificate: 'your-fixed-base64-encoded-certificate' # optional (default: unset)
subscribeEnabled: false | true # optional (default: false)
useHeartbeat: false | true # optional (default: false)
pollRate: 1000 # optional (default: 1000) The rate in milliseconds at which to poll the OPC UA server when not using subscriptions
Expand Down Expand Up @@ -167,14 +167,23 @@ input:
password: 'your-password'
```
##### Security Mode and Security Policy
##### Security Options
Security Mode: This defines the level of security applied to the messages. The options are:
- None: No security is applied; messages are neither signed nor encrypted.
- Sign: Messages are signed for integrity and authenticity but not encrypted.
- SignAndEncrypt: Provides the highest security level where messages are both signed and encrypted.
Security Policy: Specifies the set of cryptographic algorithms used for securing messages. This includes algorithms for encryption, decryption, and signing of messages. Currently only Basic256Sha256 is allowed.
Security Policy: Specifies the set of cryptographic algorithms used for securing messages. This includes algorithms for encryption, decryption, and signing of messages. The options are:
- None: No security is applied
- Basic128Rsa15 **(depracated)**: This security policy should be only used as fallback since the signature algorithm Sha1 is depracated. By default OPC-UA-Servers have disabled this option, so this is not recommended!
- Basic256 **(depracated)**: This security policy should be only used as a fallback since the signature algorithm Sha1 is depracated. By default OPC-UA-Servers have disabled this option, so this is not recommended!
- Basic256Sha256: Provides the highest security level which uses Sha256 signature algorithm.
Client Certificate: When using encryption, a client certificate is required to establish a secure connection with the OPC UA server. If no certificate is provided, the application will generate a new one upon startup, encode it in Base64, and print it out. You must manually add this certificate to the OPC UA server's trusted certificates to allow secure communication.
To avoid repeating this process every time you restart the application, copy the printed Base64-encoded certificate and paste it into your configuration under `clientCertificate`. This ensures that the same certificate is reused across sessions, preventing unnecessary certificate regeneration and avoiding potential issues with certificate storage on the OPC UA server.


While the security mode and policy are automatically selected based on the endpoint and authentication method, you have the option to override this by specifying them in the configuration file:

Expand All @@ -185,12 +194,9 @@ input:
nodeIDs: ['ns=2;s=IoTSensors']
securityMode: SignAndEncrypt
securityPolicy: Basic256Sha256
clientCertificate: 'your-fixed-base64-encoded-certificate'
```

##### Insecure Mode
This is now deprecated. By default, benthos-umh will now connect via SignAndEncrypt and Basic256Sha256 and if this fails it will fall back to insecure mode.
##### Pull and Subscribe Methods

Benthos-umh supports two modes of operation: pull and subscribe. In pull mode, it pulls all nodes every second, regardless of changes. In subscribe mode, it only sends data when there's a change in value, reducing unnecessary data transfer.
Expand Down
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ require (
go.uber.org/zap v1.27.0 // indirect
golang.org/x/mod v0.20.0 // indirect
golang.org/x/oauth2 v0.22.0 // indirect
golang.org/x/term v0.27.0 // indirect
golang.org/x/term v0.28.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
google.golang.org/api v0.188.0 // indirect
Expand Down Expand Up @@ -370,11 +370,11 @@ require (
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect
go.opentelemetry.io/otel v1.28.0 // indirect
go.opentelemetry.io/otel/trace v1.28.0 // indirect
golang.org/x/crypto v0.31.0 // indirect
golang.org/x/crypto v0.32.0 // indirect
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa
golang.org/x/net v0.33.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/tools v0.24.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
Expand Down
12 changes: 6 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1228,8 +1228,8 @@ golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
Expand Down Expand Up @@ -1385,8 +1385,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
Expand All @@ -1395,8 +1395,8 @@ golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
Expand Down
74 changes: 62 additions & 12 deletions opcua_plugin/authentication.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,44 @@ func (g *OPCUAInput) orderEndpoints(
selectedAuthentication ua.UserTokenType,
) []*ua.EndpointDescription {

var highSecurityEndpoints, noSecurityEndpoints []*ua.EndpointDescription
var (
signAndEncryptBasic256Sha256Endpoints []*ua.EndpointDescription
signBasic256Sha256Endpoints []*ua.EndpointDescription
signAndEncryptBasic256Endpoints []*ua.EndpointDescription
signBasic256Endpoints []*ua.EndpointDescription
signAndEncryptBasic128Rsa15Endpoints []*ua.EndpointDescription
signBasic128Rsa15Endpoints []*ua.EndpointDescription
noSecurityEndpoints []*ua.EndpointDescription
)

for _, endpoint := range endpoints {
if isUserTokenSupported(endpoint, selectedAuthentication) {
switch {
case isSignAndEncryptbasic256Sha256Endpoint(endpoint):
highSecurityEndpoints = append(highSecurityEndpoints, endpoint)
case isSignAndEncryptBasic256Sha256Endpoint(endpoint):
signAndEncryptBasic256Sha256Endpoints = append(signAndEncryptBasic256Sha256Endpoints, endpoint)
case isSignBasic256Sha256Endpoint(endpoint):
signBasic256Sha256Endpoints = append(signBasic256Sha256Endpoints, endpoint)
case isSignAndEncryptBasic256Endpoint(endpoint):
signAndEncryptBasic256Endpoints = append(signAndEncryptBasic256Endpoints, endpoint)
case isSignBasic256Endpoint(endpoint):
signBasic256Endpoints = append(signBasic256Endpoints, endpoint)
case isSignAndEncryptBasic128Rsa15Endpoint(endpoint):
signAndEncryptBasic128Rsa15Endpoints = append(signAndEncryptBasic128Rsa15Endpoints, endpoint)
case isSignBasic128Rsa15Endpoint(endpoint):
signBasic128Rsa15Endpoints = append(signBasic128Rsa15Endpoints, endpoint)
case isNoSecurityEndpoint(endpoint):
noSecurityEndpoints = append(noSecurityEndpoints, endpoint)
}
}
}

// Append no security endpoints to the end of the high security endpoints.
orderedEndpoints := append(highSecurityEndpoints, noSecurityEndpoints...)
// Append medium security endpoints to the end of the high security endpoints.
orderedEndpoints := append(signAndEncryptBasic256Sha256Endpoints, signBasic256Sha256Endpoints...)
orderedEndpoints = append(orderedEndpoints, signAndEncryptBasic256Endpoints...)
orderedEndpoints = append(orderedEndpoints, signAndEncryptBasic128Rsa15Endpoints...)
orderedEndpoints = append(orderedEndpoints, signBasic256Endpoints...)
orderedEndpoints = append(orderedEndpoints, signBasic128Rsa15Endpoints...)
orderedEndpoints = append(orderedEndpoints, noSecurityEndpoints...)

return orderedEndpoints
}
Expand All @@ -45,16 +68,41 @@ func isUserTokenSupported(endpoint *ua.EndpointDescription, selectedAuth ua.User
return false
}

// isSignAndEncryptbasic256Sha256Endpoint checks if the endpoint is configured with SignAndEncrypt and Basic256Sha256 security.
func isSignAndEncryptbasic256Sha256Endpoint(endpoint *ua.EndpointDescription) bool {
return endpoint.SecurityMode == ua.MessageSecurityModeFromString("SignAndEncrypt") &&
endpoint.SecurityPolicyURI == "http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256"
// isSignAndEncryptBasic256Sha256Endpoint checks if the endpoint is configured with SignAndEncrypt and Basic256Sha256 security.
func isSignAndEncryptBasic256Sha256Endpoint(endpoint *ua.EndpointDescription) bool {
return endpoint.SecurityMode == ua.MessageSecurityModeSignAndEncrypt &&
endpoint.SecurityPolicyURI == ua.SecurityPolicyURIBasic256Sha256
}

func isSignBasic256Sha256Endpoint(endpoint *ua.EndpointDescription) bool {
return endpoint.SecurityMode == ua.MessageSecurityModeSign &&
endpoint.SecurityPolicyURI == ua.SecurityPolicyURIBasic256Sha256
}

func isSignAndEncryptBasic256Endpoint(endpoint *ua.EndpointDescription) bool {
return endpoint.SecurityMode == ua.MessageSecurityModeSignAndEncrypt &&
endpoint.SecurityPolicyURI == ua.SecurityPolicyURIBasic256
}

func isSignBasic256Endpoint(endpoint *ua.EndpointDescription) bool {
return endpoint.SecurityMode == ua.MessageSecurityModeSign &&
endpoint.SecurityPolicyURI == ua.SecurityPolicyURIBasic256
}

func isSignAndEncryptBasic128Rsa15Endpoint(endpoint *ua.EndpointDescription) bool {
return endpoint.SecurityMode == ua.MessageSecurityModeSignAndEncrypt &&
endpoint.SecurityPolicyURI == ua.SecurityPolicyURIBasic128Rsa15
}

func isSignBasic128Rsa15Endpoint(endpoint *ua.EndpointDescription) bool {
return endpoint.SecurityMode == ua.MessageSecurityModeSign &&
endpoint.SecurityPolicyURI == ua.SecurityPolicyURIBasic128Rsa15
}

// isNoSecurityEndpoint checks if the endpoint has no security configured.
func isNoSecurityEndpoint(endpoint *ua.EndpointDescription) bool {
return endpoint.SecurityMode == ua.MessageSecurityModeFromString("None") &&
endpoint.SecurityPolicyURI == "http://opcfoundation.org/UA/SecurityPolicy#None"
return endpoint.SecurityMode == ua.MessageSecurityModeNone &&
endpoint.SecurityPolicyURI == ua.SecurityPolicyURINone
}

// getEndpointIfExists searches within the provided endpoints for a suitable OPC UA endpoint.
Expand All @@ -78,7 +126,9 @@ func (g *OPCUAInput) getEndpointIfExists(
for _, userIdentity := range endpoint.UserIdentityTokens {

// Match the endpoint with the selected authentication type.
if selectedAuthentication == userIdentity.TokenType && endpoint.SecurityPolicyURI == "http://opcfoundation.org/UA/SecurityPolicy#"+securityPolicy && endpoint.SecurityMode == ua.MessageSecurityModeFromString(securityMode) {
if selectedAuthentication == userIdentity.TokenType &&
endpoint.SecurityPolicyURI == ua.FormatSecurityPolicyURI(securityPolicy) &&
endpoint.SecurityMode == ua.MessageSecurityModeFromString(securityMode) {

return endpoint, nil
}
Expand Down
Loading

0 comments on commit 4a0c2d5

Please sign in to comment.