feat(common.opcua): Add support for secret-store secrets (#12863)

This commit is contained in:
Sven Rebhan 2023-03-21 15:58:06 +01:00 committed by GitHub
parent 1ccad43d6b
commit bd5f6b7c7c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 39 additions and 28 deletions

View File

@ -24,8 +24,8 @@ type OpcUAClientConfig struct {
SecurityMode string `toml:"security_mode"` SecurityMode string `toml:"security_mode"`
Certificate string `toml:"certificate"` Certificate string `toml:"certificate"`
PrivateKey string `toml:"private_key"` PrivateKey string `toml:"private_key"`
Username string `toml:"username"` Username config.Secret `toml:"username"`
Password string `toml:"password"` Password config.Secret `toml:"password"`
AuthMethod string `toml:"auth_method"` AuthMethod string `toml:"auth_method"`
ConnectTimeout config.Duration `toml:"connect_timeout"` ConnectTimeout config.Duration `toml:"connect_timeout"`
RequestTimeout config.Duration `toml:"request_timeout"` RequestTimeout config.Duration `toml:"request_timeout"`

View File

@ -19,6 +19,7 @@ import (
"github.com/gopcua/opcua" "github.com/gopcua/opcua"
"github.com/gopcua/opcua/debug" "github.com/gopcua/opcua/debug"
"github.com/gopcua/opcua/ua" "github.com/gopcua/opcua/ua"
"github.com/influxdata/telegraf/config"
) )
// SELF SIGNED CERT FUNCTIONS // SELF SIGNED CERT FUNCTIONS
@ -288,42 +289,42 @@ func (o *OpcUAClient) generateClientOpts(endpoints []*ua.EndpointDescription) ([
return opts, nil return opts, nil
} }
func (o *OpcUAClient) generateAuth(a string, cert []byte, un, pw string) (ua.UserTokenType, opcua.Option, error) { func (o *OpcUAClient) generateAuth(a string, cert []byte, user, passwd config.Secret) (ua.UserTokenType, opcua.Option, error) {
var err error
var authMode ua.UserTokenType var authMode ua.UserTokenType
var authOption opcua.Option var authOption opcua.Option
switch strings.ToLower(a) { switch strings.ToLower(a) {
case "anonymous": case "anonymous":
authMode = ua.UserTokenTypeAnonymous authMode = ua.UserTokenTypeAnonymous
authOption = opcua.AuthAnonymous() authOption = opcua.AuthAnonymous()
case "username": case "username":
authMode = ua.UserTokenTypeUserName authMode = ua.UserTokenTypeUserName
if un == "" { var username, password []byte
if !user.Empty() {
var err error
username, err = user.Get()
if err != nil { if err != nil {
return 0, nil, fmt.Errorf("error reading the username input: %w", err) return 0, nil, fmt.Errorf("error reading the username input: %w", err)
} }
defer config.ReleaseSecret(username)
} }
if pw == "" { if !passwd.Empty() {
var err error
password, err = passwd.Get()
if err != nil { if err != nil {
return 0, nil, fmt.Errorf("error reading the password input: %w", err) return 0, nil, fmt.Errorf("error reading the password input: %w", err)
} }
defer config.ReleaseSecret(password)
} }
authOption = opcua.AuthUsername(string(username), string(password))
authOption = opcua.AuthUsername(un, pw)
case "certificate": case "certificate":
authMode = ua.UserTokenTypeCertificate authMode = ua.UserTokenTypeCertificate
authOption = opcua.AuthCertificate(cert) authOption = opcua.AuthCertificate(cert)
case "issuedtoken": case "issuedtoken":
// todo: this is unsupported, fail here or fail in the opcua package? // todo: this is unsupported, fail here or fail in the opcua package?
authMode = ua.UserTokenTypeIssuedToken authMode = ua.UserTokenTypeIssuedToken
authOption = opcua.AuthIssuedToken([]byte(nil)) authOption = opcua.AuthIssuedToken([]byte(nil))
default: default:
o.Log.Warnf("unknown auth-mode, defaulting to Anonymous") o.Log.Warnf("unknown auth-mode, defaulting to Anonymous")
authMode = ua.UserTokenTypeAnonymous authMode = ua.UserTokenTypeAnonymous

View File

@ -14,6 +14,15 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
[CONFIGURATION.md]: ../../../docs/CONFIGURATION.md#plugins [CONFIGURATION.md]: ../../../docs/CONFIGURATION.md#plugins
## Secret-store support
This plugin supports secrets from secret-stores for the `username` and
`password` option.
See the [secret-store documentation][SECRETSTORE] for more details on how
to use them.
[SECRETSTORE]: ../../../docs/CONFIGURATION.md#secret-store-secrets
## Configuration ## Configuration
```toml @sample.conf ```toml @sample.conf

View File

@ -62,10 +62,6 @@ func TestGetDataBadNodeContainerIntegration(t *testing.T) {
Endpoint: fmt.Sprintf("opc.tcp://%s:%s", container.Address, container.Ports[servicePort]), Endpoint: fmt.Sprintf("opc.tcp://%s:%s", container.Address, container.Ports[servicePort]),
SecurityPolicy: "None", SecurityPolicy: "None",
SecurityMode: "None", SecurityMode: "None",
Certificate: "",
PrivateKey: "",
Username: "",
Password: "",
AuthMethod: "Anonymous", AuthMethod: "Anonymous",
ConnectTimeout: config.Duration(10 * time.Second), ConnectTimeout: config.Duration(10 * time.Second),
RequestTimeout: config.Duration(1 * time.Second), RequestTimeout: config.Duration(1 * time.Second),
@ -128,10 +124,6 @@ func TestReadClientIntegration(t *testing.T) {
Endpoint: fmt.Sprintf("opc.tcp://%s:%s", container.Address, container.Ports[servicePort]), Endpoint: fmt.Sprintf("opc.tcp://%s:%s", container.Address, container.Ports[servicePort]),
SecurityPolicy: "None", SecurityPolicy: "None",
SecurityMode: "None", SecurityMode: "None",
Certificate: "",
PrivateKey: "",
Username: "",
Password: "",
AuthMethod: "Anonymous", AuthMethod: "Anonymous",
ConnectTimeout: config.Duration(10 * time.Second), ConnectTimeout: config.Duration(10 * time.Second),
RequestTimeout: config.Duration(1 * time.Second), RequestTimeout: config.Duration(1 * time.Second),
@ -188,8 +180,8 @@ func TestReadClientIntegrationWithPasswordAuth(t *testing.T) {
Endpoint: fmt.Sprintf("opc.tcp://%s:%s", container.Address, container.Ports[servicePort]), Endpoint: fmt.Sprintf("opc.tcp://%s:%s", container.Address, container.Ports[servicePort]),
SecurityPolicy: "None", SecurityPolicy: "None",
SecurityMode: "None", SecurityMode: "None",
Username: "peter", Username: config.NewSecret([]byte("peter")),
Password: "peter123", Password: config.NewSecret([]byte("peter123")),
AuthMethod: "UserName", AuthMethod: "UserName",
ConnectTimeout: config.Duration(10 * time.Second), ConnectTimeout: config.Duration(10 * time.Second),
RequestTimeout: config.Duration(1 * time.Second), RequestTimeout: config.Duration(1 * time.Second),
@ -293,8 +285,8 @@ use_unregistered_reads = true
require.Equal(t, "/etc/telegraf/cert.pem", o.ReadClientConfig.Certificate) require.Equal(t, "/etc/telegraf/cert.pem", o.ReadClientConfig.Certificate)
require.Equal(t, "/etc/telegraf/key.pem", o.ReadClientConfig.PrivateKey) require.Equal(t, "/etc/telegraf/key.pem", o.ReadClientConfig.PrivateKey)
require.Equal(t, "Anonymous", o.ReadClientConfig.AuthMethod) require.Equal(t, "Anonymous", o.ReadClientConfig.AuthMethod)
require.Equal(t, "", o.ReadClientConfig.Username) require.True(t, o.ReadClientConfig.Username.Empty())
require.Equal(t, "", o.ReadClientConfig.Password) require.True(t, o.ReadClientConfig.Password.Empty())
require.Equal(t, []input.NodeSettings{ require.Equal(t, []input.NodeSettings{
{ {
FieldName: "name", FieldName: "name",

View File

@ -14,6 +14,15 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
[CONFIGURATION.md]: ../../../docs/CONFIGURATION.md#plugins [CONFIGURATION.md]: ../../../docs/CONFIGURATION.md#plugins
## Secret-store support
This plugin supports secrets from secret-stores for the `username` and
`password` option.
See the [secret-store documentation][SECRETSTORE] for more details on how
to use them.
[SECRETSTORE]: ../../../docs/CONFIGURATION.md#secret-store-secrets
## Configuration ## Configuration
```toml @sample.conf ```toml @sample.conf

View File

@ -205,8 +205,8 @@ additional_valid_status_codes = ["0xC0"]
require.Equal(t, "/etc/telegraf/cert.pem", o.SubscribeClientConfig.Certificate) require.Equal(t, "/etc/telegraf/cert.pem", o.SubscribeClientConfig.Certificate)
require.Equal(t, "/etc/telegraf/key.pem", o.SubscribeClientConfig.PrivateKey) require.Equal(t, "/etc/telegraf/key.pem", o.SubscribeClientConfig.PrivateKey)
require.Equal(t, "Anonymous", o.SubscribeClientConfig.AuthMethod) require.Equal(t, "Anonymous", o.SubscribeClientConfig.AuthMethod)
require.Equal(t, "", o.SubscribeClientConfig.Username) require.True(t, o.SubscribeClientConfig.Username.Empty())
require.Equal(t, "", o.SubscribeClientConfig.Password) require.True(t, o.SubscribeClientConfig.Password.Empty())
require.Equal(t, []input.NodeSettings{ require.Equal(t, []input.NodeSettings{
{ {
FieldName: "name", FieldName: "name",

View File

@ -141,7 +141,7 @@ func (o *SubscribeClient) processReceivedNotifications() {
i := int(monitoredItemNotif.ClientHandle) i := int(monitoredItemNotif.ClientHandle)
oldValue := o.LastReceivedData[i].Value oldValue := o.LastReceivedData[i].Value
o.UpdateNodeValue(i, monitoredItemNotif.Value) o.UpdateNodeValue(i, monitoredItemNotif.Value)
o.Log.Debugf("Data change notification: node %q value changed from %f to %f", o.Log.Debugf("Data change notification: node %q value changed from %v to %v",
o.NodeIDs[i].String(), oldValue, o.LastReceivedData[i].Value) o.NodeIDs[i].String(), oldValue, o.LastReceivedData[i].Value)
o.metrics <- o.MetricForNode(i) o.metrics <- o.MetricForNode(i)
} }