Skip to content

Commit

Permalink
Merge pull request #8310 from mohamedawnallah/lnconfig-support-env-va…
Browse files Browse the repository at this point in the history
…rs-for-rpcuser-rpcpassword

lnconfig: Support utilizing Environment Variables in `lnd.conf` for `rpcuser` and `rpcpass` fields.
  • Loading branch information
guggero committed Mar 15, 2024
2 parents ff31426 + 0add4fc commit 51ebc20
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 2 deletions.
80 changes: 78 additions & 2 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -1779,6 +1779,11 @@ func parseRPCParams(cConfig *lncfg.Chain, nodeConfig interface{},
var daemonName, confDir, confFile, confFileBase string
switch conf := nodeConfig.(type) {
case *lncfg.Btcd:
// Resolves environment variable references in RPCUser and
// RPCPass fields.
conf.RPCUser = supplyEnvValue(conf.RPCUser)
conf.RPCPass = supplyEnvValue(conf.RPCPass)

// If both RPCUser and RPCPass are set, we assume those
// credentials are good to use.
if conf.RPCUser != "" && conf.RPCPass != "" {
Expand Down Expand Up @@ -1824,6 +1829,11 @@ func parseRPCParams(cConfig *lncfg.Chain, nodeConfig interface{},
confFile = conf.ConfigPath
confFileBase = BitcoinChainName

// Resolves environment variable references in RPCUser
// and RPCPass fields.
conf.RPCUser = supplyEnvValue(conf.RPCUser)
conf.RPCPass = supplyEnvValue(conf.RPCPass)

// Check that cookie and credentials don't contradict each
// other.
if (conf.RPCUser != "" || conf.RPCPass != "") &&
Expand Down Expand Up @@ -1919,6 +1929,70 @@ func parseRPCParams(cConfig *lncfg.Chain, nodeConfig interface{},
return nil
}

// supplyEnvValue supplies the value of an environment variable from a string.
// It supports the following formats:
// 1) $ENV_VAR
// 2) ${ENV_VAR}
// 3) ${ENV_VAR:-DEFAULT}
//
// Standard environment variable naming conventions:
// - ENV_VAR contains letters, digits, and underscores, and does
// not start with a digit.
// - DEFAULT follows the rule that it can contain any characters except
// whitespace.
//
// Parameters:
// - value: The input string containing references to environment variables
// (if any).
//
// Returns:
// - string: The value of the specified environment variable, the default
// value if provided, or the original input string if no matching variable is
// found or set.
func supplyEnvValue(value string) string {
// Regex for $ENV_VAR format.
var reEnvVar = regexp.MustCompile(`^\$([a-zA-Z_][a-zA-Z0-9_]*)$`)

// Regex for ${ENV_VAR} format.
var reEnvVarWithBrackets = regexp.MustCompile(
`^\$\{([a-zA-Z_][a-zA-Z0-9_]*)\}$`,
)

// Regex for ${ENV_VAR:-DEFAULT} format.
var reEnvVarWithDefault = regexp.MustCompile(
`^\$\{([a-zA-Z_][a-zA-Z0-9_]*):-([\S]+)\}$`,
)

// Match against supported formats.
switch {
case reEnvVarWithDefault.MatchString(value):
matches := reEnvVarWithDefault.FindStringSubmatch(value)
envVariable := matches[1]
defaultValue := matches[2]
if envValue := os.Getenv(envVariable); envValue != "" {
return envValue
}

return defaultValue

case reEnvVarWithBrackets.MatchString(value):
matches := reEnvVarWithBrackets.FindStringSubmatch(value)
envVariable := matches[1]
envValue := os.Getenv(envVariable)

return envValue

case reEnvVar.MatchString(value):
matches := reEnvVar.FindStringSubmatch(value)
envVariable := matches[1]
envValue := os.Getenv(envVariable)

return envValue
}

return value
}

// extractBtcdRPCParams attempts to extract the RPC credentials for an existing
// btcd instance. The passed path is expected to be the location of btcd's
// application data directory on the target system.
Expand Down Expand Up @@ -1962,7 +2036,8 @@ func extractBtcdRPCParams(btcdConfigPath string) (string, string, error) {
return "", "", fmt.Errorf("unable to find rpcuser in config")
}

return string(userSubmatches[1]), string(passSubmatches[1]), nil
return supplyEnvValue(string(userSubmatches[1])),
supplyEnvValue(string(passSubmatches[1])), nil
}

// extractBitcoindRPCParams attempts to extract the RPC credentials for an
Expand Down Expand Up @@ -2087,7 +2162,8 @@ func extractBitcoindRPCParams(networkName, bitcoindDataDir, bitcoindConfigPath,
"in config")
}

return string(userSubmatches[1]), string(passSubmatches[1]),
return supplyEnvValue(string(userSubmatches[1])),
supplyEnvValue(string(passSubmatches[1])),
zmqBlockHost, zmqTxHost, nil
}

Expand Down
63 changes: 63 additions & 0 deletions config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,66 @@ func TestConfigToFlatMap(t *testing.T) {
require.Equal(t, redactedPassword, result["db.etcd.pass"])
require.Equal(t, redactedPassword, result["db.postgres.dsn"])
}

// TestSupplyEnvValue tests that the supplyEnvValue function works as
// expected on the passed inputs.
func TestSupplyEnvValue(t *testing.T) {
// Mock environment variables for testing.
t.Setenv("EXISTING_VAR", "existing_value")
t.Setenv("EMPTY_VAR", "")

tests := []struct {
input string
expected string
description string
}{
{
input: "$EXISTING_VAR",
expected: "existing_value",
description: "Valid environment variable without " +
"default value",
},
{
input: "${EXISTING_VAR:-default_value}",
expected: "existing_value",
description: "Valid environment variable with " +
"default value",
},
{
input: "$NON_EXISTENT_VAR",
expected: "",
description: "Non-existent environment variable " +
"without default value",
},
{
input: "${NON_EXISTENT_VAR:-default_value}",
expected: "default_value",
description: "Non-existent environment variable " +
"with default value",
},
{
input: "$EMPTY_VAR",
expected: "",
description: "Empty environment variable without " +
"default value",
},
{
input: "${EMPTY_VAR:-default_value}",
expected: "default_value",
description: "Empty environment variable with " +
"default value",
},
{
input: "raw_input",
expected: "raw_input",
description: "Raw input - no matching format",
},
}

for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
result := supplyEnvValue(test.input)
require.Equal(t, test.expected, result)
})
}
}
3 changes: 3 additions & 0 deletions docs/release-notes/release-notes-0.18.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,9 @@
funding operations and the new `PsbtCoinSelect` option of the `FundPsbt`
RPC](https://github.com/lightningnetwork/lnd/pull/8378).

* [Env Variables in lnd.conf](https://github.com/lightningnetwork/lnd/pull/8310)
Support utilizing the usage of environment variables in `lnd.conf` for `rpcuser` and `rpcpass` fields to better protect the secrets.

## RPC Additions

* [Deprecated](https://github.com/lightningnetwork/lnd/pull/7175)
Expand Down

0 comments on commit 51ebc20

Please sign in to comment.