mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-06-06 21:20:32 +02:00
Merge pull request #8310 from mohamedawnallah/lnconfig-support-env-vars-for-rpcuser-rpcpassword
lnconfig: Support utilizing Environment Variables in `lnd.conf` for `rpcuser` and `rpcpass` fields.
This commit is contained in:
commit
51ebc2052f
80
config.go
80
config.go
@ -1779,6 +1779,11 @@ func parseRPCParams(cConfig *lncfg.Chain, nodeConfig interface{},
|
|||||||
var daemonName, confDir, confFile, confFileBase string
|
var daemonName, confDir, confFile, confFileBase string
|
||||||
switch conf := nodeConfig.(type) {
|
switch conf := nodeConfig.(type) {
|
||||||
case *lncfg.Btcd:
|
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
|
// If both RPCUser and RPCPass are set, we assume those
|
||||||
// credentials are good to use.
|
// credentials are good to use.
|
||||||
if conf.RPCUser != "" && conf.RPCPass != "" {
|
if conf.RPCUser != "" && conf.RPCPass != "" {
|
||||||
@ -1824,6 +1829,11 @@ func parseRPCParams(cConfig *lncfg.Chain, nodeConfig interface{},
|
|||||||
confFile = conf.ConfigPath
|
confFile = conf.ConfigPath
|
||||||
confFileBase = BitcoinChainName
|
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
|
// Check that cookie and credentials don't contradict each
|
||||||
// other.
|
// other.
|
||||||
if (conf.RPCUser != "" || conf.RPCPass != "") &&
|
if (conf.RPCUser != "" || conf.RPCPass != "") &&
|
||||||
@ -1919,6 +1929,70 @@ func parseRPCParams(cConfig *lncfg.Chain, nodeConfig interface{},
|
|||||||
return nil
|
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
|
// extractBtcdRPCParams attempts to extract the RPC credentials for an existing
|
||||||
// btcd instance. The passed path is expected to be the location of btcd's
|
// btcd instance. The passed path is expected to be the location of btcd's
|
||||||
// application data directory on the target system.
|
// application data directory on the target system.
|
||||||
@ -1962,7 +2036,8 @@ func extractBtcdRPCParams(btcdConfigPath string) (string, string, error) {
|
|||||||
return "", "", fmt.Errorf("unable to find rpcuser in config")
|
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
|
// extractBitcoindRPCParams attempts to extract the RPC credentials for an
|
||||||
@ -2087,7 +2162,8 @@ func extractBitcoindRPCParams(networkName, bitcoindDataDir, bitcoindConfigPath,
|
|||||||
"in config")
|
"in config")
|
||||||
}
|
}
|
||||||
|
|
||||||
return string(userSubmatches[1]), string(passSubmatches[1]),
|
return supplyEnvValue(string(userSubmatches[1])),
|
||||||
|
supplyEnvValue(string(passSubmatches[1])),
|
||||||
zmqBlockHost, zmqTxHost, nil
|
zmqBlockHost, zmqTxHost, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,3 +52,66 @@ func TestConfigToFlatMap(t *testing.T) {
|
|||||||
require.Equal(t, redactedPassword, result["db.etcd.pass"])
|
require.Equal(t, redactedPassword, result["db.etcd.pass"])
|
||||||
require.Equal(t, redactedPassword, result["db.postgres.dsn"])
|
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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -145,6 +145,9 @@
|
|||||||
funding operations and the new `PsbtCoinSelect` option of the `FundPsbt`
|
funding operations and the new `PsbtCoinSelect` option of the `FundPsbt`
|
||||||
RPC](https://github.com/lightningnetwork/lnd/pull/8378).
|
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
|
## RPC Additions
|
||||||
|
|
||||||
* [Deprecated](https://github.com/lightningnetwork/lnd/pull/7175)
|
* [Deprecated](https://github.com/lightningnetwork/lnd/pull/7175)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user