diff --git a/README.md b/README.md index b9980e58..a1b23c56 100644 --- a/README.md +++ b/README.md @@ -676,6 +676,9 @@ If you prefer to provide configuration via a [config file](./example-config.yaml ```yaml # Example Oracle Database Metrics Exporter Configuration file. # Environment variables of the form ${VAR_NAME} will be expanded. +# If you include a config value that contains a '$' character, escape that '$' with another '$', e.g., +# "$test$pwd" => "$$test$$pwd" +# Otherwise, the value will be expanded as an environment variable. # Example Oracle Database Metrics Exporter Configuration file. # Environment variables of the form ${VAR_NAME} will be expanded. diff --git a/changelog.md b/changelog.md index c9971a38..fdb3b092 100644 --- a/changelog.md +++ b/changelog.md @@ -6,6 +6,7 @@ Our current priorities are support for Exadata metrics. We expect to address th This release includes the following changes: - Fixed a bug where database type (CDB, PDB, etc.) was not reported in certain situations. +- Fixed a bug where literal passwords containing the '$' character (in the config file) would be evaluated as environment variables. To use literal passwords with the '$' character, escape the '$' character with a second '$': `$test$pwd` becomes `$$test$$pwd`. ### Version 2.0.2, June 24, 2025 diff --git a/collector/config.go b/collector/config.go index d6c326dd..523d6f46 100644 --- a/collector/config.go +++ b/collector/config.go @@ -136,27 +136,33 @@ func (c ConnectConfig) GetQueryTimeout() int { } func (d DatabaseConfig) GetUsername() string { - - if d.Vault.OCI.UsernameSecret != "" { + if d.isOCIVault() && d.Vault.OCI.UsernameSecret != "" { return ocivault.GetVaultSecret(d.Vault.OCI.ID, d.Vault.OCI.UsernameSecret) } - if d.Vault.Azure.UsernameSecret != "" { + if d.isAzureVault() && d.Vault.Azure.UsernameSecret != "" { return azvault.GetVaultSecret(d.Vault.Azure.ID, d.Vault.Azure.UsernameSecret) } return d.Username } func (d DatabaseConfig) GetPassword() string { - - if d.Vault.OCI.PasswordSecret != "" { + if d.isOCIVault() && d.Vault.OCI.PasswordSecret != "" { return ocivault.GetVaultSecret(d.Vault.OCI.ID, d.Vault.OCI.PasswordSecret) } - if d.Vault.Azure.PasswordSecret != "" { + if d.isAzureVault() && d.Vault.Azure.PasswordSecret != "" { return azvault.GetVaultSecret(d.Vault.Azure.ID, d.Vault.Azure.PasswordSecret) } return d.Password } +func (d DatabaseConfig) isOCIVault() bool { + return d.Vault != nil && d.Vault.OCI != nil +} + +func (d DatabaseConfig) isAzureVault() bool { + return d.Vault != nil && d.Vault.Azure != nil +} + func LoadMetricsConfiguration(logger *slog.Logger, cfg *Config, path string) (*MetricsConfiguration, error) { m := &MetricsConfiguration{} if len(cfg.ConfigFile) > 0 { @@ -164,7 +170,13 @@ func LoadMetricsConfiguration(logger *slog.Logger, cfg *Config, path string) (*M if err != nil { return m, err } - expanded := os.ExpandEnv(string(content)) + expanded := os.Expand(string(content), func(s string) string { + // allows escaping literal $ characters + if s == "$" { + return "$" + } + return os.Getenv(s) + }) if yerr := yaml.UnmarshalStrict([]byte(expanded), m); yerr != nil { return m, yerr } diff --git a/go.mod b/go.mod index c9e0ad50..68579358 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/prometheus/client_golang v1.22.0 github.com/prometheus/common v0.65.0 github.com/prometheus/exporter-toolkit v0.14.0 + gopkg.in/yaml.v2 v2.4.0 ) require ( @@ -50,5 +51,4 @@ require ( golang.org/x/sys v0.33.0 // indirect golang.org/x/text v0.25.0 // indirect google.golang.org/protobuf v1.36.6 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect )