diff --git a/common/configvar.h b/common/configvar.h index 4453f455ffd2..f58d7738895d 100644 --- a/common/configvar.h +++ b/common/configvar.h @@ -62,6 +62,8 @@ struct configvar { #define OPT_DYNAMIC (1 << (OPT_USER_START+6)) /* Keep whitespace at the end of the option argument */ #define OPT_KEEP_WHITESPACE (1 << (OPT_USER_START+7)) +/* Don't show value in listconfigs */ +#define OPT_CONCEAL (1 << (OPT_USER_START+8)) /* Use this instead of opt_register_*_arg if you want OPT_* from above */ #define clnopt_witharg(names, type, cb, show, arg, desc) \ diff --git a/doc/developers-guide/plugin-development/a-day-in-the-life-of-a-plugin.md b/doc/developers-guide/plugin-development/a-day-in-the-life-of-a-plugin.md index 4ca7df890120..039ed1de19f1 100644 --- a/doc/developers-guide/plugin-development/a-day-in-the-life-of-a-plugin.md +++ b/doc/developers-guide/plugin-development/a-day-in-the-life-of-a-plugin.md @@ -124,10 +124,11 @@ Plugins are free to register any `name` for their `rpcmethod` as long as the nam There are currently four supported option 'types': -- string: a string -- bool: a boolean -- int: parsed as a signed integer (64-bit) -- flag: no-arg flag option. Presented as `true` if config specifies it. +- `string`: a string +- `string-conceal`: a string which will appear as "..." in `listconfigs`. +- `bool`: a boolean +- `int`: parsed as a signed integer (64-bit) +- `flag`: no-arg flag option. Presented as `true` if config specifies it. In addition, string and int types can specify `"multi": true` to indicate they can be specified multiple times. These will always be represented in `init` as a (possibly empty) JSON array. "multi" flag types do not make sense. diff --git a/lightningd/configs.c b/lightningd/configs.c index 4191e57ca6f8..50be598b724e 100644 --- a/lightningd/configs.c +++ b/lightningd/configs.c @@ -65,6 +65,9 @@ static const char *get_opt_val(const struct opt_table *ot, char buf[], const struct configvar *cv) { + if (ot->type & OPT_CONCEAL) + return "..."; + if (ot->show == (void *)opt_show_charp) { /* Don't truncate or quote! */ return *(char **)ot->u.carg; diff --git a/lightningd/options.c b/lightningd/options.c index d9aa3718176b..fea774e6da99 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -2095,8 +2095,10 @@ void add_config_deprecated(struct lightningd *ld, if (!opt->show(buf, sizeof(buf) - sizeof("..."), opt->u.carg)) buf[0] = '\0'; - if ((opt->type & OPT_SHOWINT) - || (opt->type & OPT_SHOWMSATS)) { + if (opt->type & OPT_CONCEAL) { + strcpy(buf, "..."); + } else if ((opt->type & OPT_SHOWINT) + || (opt->type & OPT_SHOWMSATS)) { if (streq(buf, "") || strspn(buf, "-0123456789.") != strlen(buf)) errx(1, "Bad literal for %s: %s", name0, buf); diff --git a/lightningd/plugin.c b/lightningd/plugin.c index 1292b8914268..1c1410b60687 100644 --- a/lightningd/plugin.c +++ b/lightningd/plugin.c @@ -1158,7 +1158,10 @@ static const char *plugin_opt_add(struct plugin *plugin, const char *buffer, /* These all take an arg. */ char *(*cb_arg)(const char *optarg, void *arg); - if (json_tok_streq(buffer, typetok, "string")) { + if (json_tok_streq(buffer, typetok, "string-conceal")) { + optflags |= OPT_CONCEAL; + cb_arg = (void *)plugin_opt_string_check; + } else if (json_tok_streq(buffer, typetok, "string")) { cb_arg = (void *)plugin_opt_string_check; } else if (json_tok_streq(buffer, typetok, "int")) { cb_arg = (void *)plugin_opt_long_check; diff --git a/plugins/.gitignore b/plugins/.gitignore index a2eb382c4bf4..e38988d7d817 100644 --- a/plugins/.gitignore +++ b/plugins/.gitignore @@ -18,3 +18,4 @@ cln-renepay recover cln-askrene recklessrpc +exposesecret diff --git a/plugins/exposesecret.c b/plugins/exposesecret.c index 42876802545e..31e812816916 100644 --- a/plugins/exposesecret.c +++ b/plugins/exposesecret.c @@ -156,7 +156,7 @@ int main(int argc, char *argv[]) plugin_main(argv, init, take(exposesecret), PLUGIN_RESTARTABLE, true, NULL, commands, ARRAY_SIZE(commands), NULL, 0, NULL, 0, NULL, 0, - plugin_option("exposesecret-passphrase", "string", + plugin_option("exposesecret-passphrase", "string-conceal", "Enable exposesecret command to allow HSM Secret backup, with this passphrase", charp_option, NULL, &exposesecret->exposure_passphrase), NULL); diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 0fecb79e3d8d..74e60644a001 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -4420,6 +4420,10 @@ def test_sql_crash(node_factory, bitcoind): def test_exposesecret(node_factory): l1, l2 = node_factory.get_nodes(2, opts=[{'exposesecret-passphrase': "test_exposesecret"}, {}]) + # listconfigs will conceal the value for us, even if we ask directly. + l1.rpc.listconfigs()['configs']['exposesecret-passphrase']['value_str'] == '...' + l1.rpc.listconfigs('exposesecret-passphrase')['configs']['exposesecret-passphrase']['value_str'] == '...' + # l2 won't expose the secret! with pytest.raises(RpcError, match="exposesecrets-passphrase is not set"): l2.rpc.exposesecret(passphrase='test_exposesecret')