Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Log RPC parameters (arguments) if -debug=rpcparams #16365

Closed
wants to merge 1 commit into from

Conversation

LarryRuane
Copy link
Contributor

@LarryRuane LarryRuane commented Jul 9, 2019

When a developer is examining debug.log (or client terminal output), it's often useful to know which RPCs have been submitted to the client; this can be enabled with the -debug=rpc configuration option. But this prints only the method name. This PR adds -debug=rpcparams to enable the logging of each RPC's parameters (arguments). The parameters of certain RPCs are keys or passwords; these should not be logged.

"encryptwallet",
"walletpassphrase",
"walletpassphrasechange",
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this list is incomplete. Would be nice if it can be deduplicated with the existing QStringList historyFilter

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This blacklist should be pulled out of a config rather than buried in the code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean by "config"? Are you referring to ~/.bitcoin/bitcoin.conf?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean that it should go into a common module, probably ./src/util/ or similar.

@DrahtBot
Copy link
Contributor

DrahtBot commented Jul 9, 2019

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Conflicts

Reviewers, this pull request conflicts with the following ones:

  • #22736 (log, sync: change lock contention from preprocessor directive to log category by jonatack)
  • #20487 (Add syscall sandboxing using seccomp-bpf (Linux secure computing mode) by practicalswift)

If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

@promag
Copy link
Member

promag commented Jul 10, 2019

How about private keys?

Copy link
Contributor Author

@LarryRuane LarryRuane left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rebased, addressed review comments, force-pushed.

src/logging.h Outdated
@@ -54,6 +54,7 @@ namespace BCLog {
COINDB = (1 << 18),
QT = (1 << 19),
LEVELDB = (1 << 20),
RPCPARAMS = (1 << 21),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm undecided if a new category is needed, or just enable this additional logging with rpc. We don't have many of the 32 category bits remaining (although we could easily change to use a uint64_t).

LogPrint(BCLog::RPCPARAMS, ",");
LogPrint(BCLog::RPCPARAMS, "%s", SanitizeString(params[i].getValStr()));
}
LogPrint(BCLog::RPCPARAMS, "]\n");
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could print each param on a separate line.

@promag
Copy link
Member

promag commented Jul 12, 2019

IMO the blacklist doesn't look a great option.

How about disallow -debug=rpcparams in mainnet and drop the blacklist?

@instagibbs
Copy link
Member

concept NACK, someone is going to forget when adding a new RPC and start logging secrets in production.

I think @promag suggestion is better.

@LarryRuane LarryRuane force-pushed the log-rpc-args branch 2 times, most recently from 6ab0383 to e484051 Compare July 13, 2019 15:15
@promag
Copy link
Member

promag commented Aug 5, 2019

Another approach is to add a sensitive options/flag (false by default) to RPCHelpMan RPCArg.

@LarryRuane
Copy link
Contributor Author

@promag, interesting idea, I like that it would allow specific arguments to be designated as sensitive, rather than the entire method. Also, it would be nice not to have to touch so many lines of code (as my latest commit does). But @instagibbs's concern, #16365 (comment), which I was attempted to overcome with my latest commit, becomes live again.

@LarryRuane
Copy link
Contributor Author

Rebased, replaced functional (python) test with a proper unit test.

@LarryRuane
Copy link
Contributor Author

I had an idea for what may be an improvement. It's in a separate commit, "replace sensitive bool with more general flags". Please let me know what you think; this commit is optional.

Modifying all of the lines of the static const CRPCCommand commands[] tables is disruptive, and all that's being done is to add a boolean value (to indicate whether to log the RPC's params or not). As long as we're touching all these lines anyway, why not implement a more general flags bitfield, of which DONTLOG is just the first bit. There may be other future attributes that would be useful to add to RPC methods. With this commit, adding them would touch only the lines corresponding to the affected RPCs, rather than all of them.

@LarryRuane
Copy link
Contributor Author

Force-pushed (diff) a different approach that does not require changing each of the 149 per-rpc CRPCCommand initializers. This new way is much less invasive and more manageable. I finally decided it's best to go with an "allow" list (hard to beat simplicity). If someone adds a new RPC later without being aware of this form of logging, its arguments will not be logged. That makes this approach very safe against accidentally leaking secret information to the log file. I incorporated some of the review comments as well.

Here are some examples (without showing the RPC output):

$ bitcoin-cli getnetworkinfo
$ bitcoin-cli getblockhash 0
$ bitcoin-cli logging '["rpcparams"]' '["rpc","mempool"]'
$ bitcoin-cli encryptwallet WALLET-PASSWORD-SECRET

Here's what appears in debug.log:

2020-09-29T05:58:50Z rpc=getnetworkinfo()
2020-09-29T05:59:00Z rpc=getblockhash(0)
2020-09-29T06:02:51Z rpc=logging([rpcparams],[rpc,mempool])
2020-09-29T06:02:55Z rpc=encryptwallet(****)

@LarryRuane LarryRuane changed the title [WIP] Log RPC parameters (arguments) if -debug=rpcparams Log RPC parameters (arguments) if -debug=rpcparams Sep 29, 2020
"walletcreatefundedpsbt",
"walletlock",
"walletprocesspsbt",
};
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This allow list came from listing all the RPCs and then removing the following:

signmessagewithprivkey
signrawtransactionwithkey
signrawtransactionwithwallet
signmessage
createwallet
encryptwallet
importmulti
importprivkey
sethdseed
walletpassphrase
walletpassphrasechange

As far as I can tell, these are the RPCs whose arguments shouldn't be logged.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maintainence of this list seems like a drawback. It would be nice if a test fails somewhere when an RPC is added or removed without the allow list being updated, e.g. with a list of structs of all the RPCs with an associated safe/unsafe bool value, or something like CRPCConvertParam. Feel to ignore if unrealistic.

Copy link
Member

@jonatack jonatack left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested almost-ACK 7d22a6f rebased to current master, modulo the comments below

@@ -59,6 +59,7 @@ namespace BCLog {
VALIDATION = (1 << 21),
I2P = (1 << 22),
IPC = (1 << 23),
RPCPARAMS = (1 << 24),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test/functional/rpc_misc.py::L60 needs to be updated: AssertionError: not(25 == 24)

"psbtbumpfee",
"reconsiderblock",
"removeprunedfunds",
"rescanblockchain",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This list seems to need an update for new RPCS (restorewallet comes to mind, not sure if there are others)

"walletcreatefundedpsbt",
"walletlock",
"walletprocesspsbt",
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maintainence of this list seems like a drawback. It would be nice if a test fails somewhere when an RPC is added or removed without the allow list being updated, e.g. with a list of structs of all the RPCs with an associated safe/unsafe bool value, or something like CRPCConvertParam. Feel to ignore if unrealistic.

params += ",";
}
params += request.params[i].write();
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe replace this loop with a call to util/string.h::Join()

BOOST_CHECK(str.find("rpc=signmessagewithprivkey(****)") != std::string::npos);
// Make sure these don't somehow appear
BOOST_CHECK(str.find("some-key") == std::string::npos);
BOOST_CHECK(str.find("some-message") == std::string::npos);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some (tested) ideas, feel free to pick/choose/ignore

-    FILE* file = fsbridge::fopen(LogInstance().m_file_path, "rb");
+    FILE* file{fsbridge::fopen(LogInstance().m_file_path, "rb")};
     fseek(file, 0, SEEK_END);
     std::vector<char> vch(ftell(file), 0);
     fseek(file, 0, SEEK_SET);
-    size_t nbytes = fread(vch.data(), 1, vch.size(), file);
+    const size_t nbytes{fread(vch.data(), 1, vch.size(), file)};
     fclose(file);
 
     // This checks the test (not the code being tested).
     BOOST_CHECK_EQUAL(nbytes, vch.size());
 
     // Check that what should appear does, and what shouldn't doesn't.
-    std::string str(vch.begin(), vch.end());
+    const std::string str{vch.begin(), vch.end()};
@@ -512,8 +512,8 @@ BOOST_AUTO_TEST_CASE(rpc_logparams)
-    BOOST_CHECK(str.find("some-key") == std::string::npos);
-    BOOST_CHECK(str.find("some-message") == std::string::npos);
+    BOOST_CHECK_EQUAL(str.find("some-key"), std::string::npos);
+    BOOST_CHECK_EQUAL(str.find("some-message"), std::string::npos);

} else {
for (size_t i = 0; i < request.params.size(); ++i) {
if (i > 0) {
params += ",";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure here, perhaps separate the params with ", " (comma + space)

now

2021-08-19T11:45:46Z rpc=getmempoolancestors(b6a5ed05bc71c8ccc5316,true)

comma+space

2021-08-19T11:45:46Z rpc=getmempoolancestors(b6a5ed05bc71c8ccc5316, true)

@LarryRuane
Copy link
Contributor Author

Maintenance of this list seems like a drawback

I agree, I'm going to convert this PR to draft because I think there's a better way to do this that doesn't require a separate list. I'll also pick up your other suggestions. Thanks for taking the time to look it over!

@LarryRuane LarryRuane marked this pull request as draft August 20, 2021 15:37
@DrahtBot
Copy link
Contributor

🐙 This pull request conflicts with the target branch and needs rebase.

Want to unsubscribe from rebase notifications on this pull request? Just convert this pull request to a "draft".

@DrahtBot
Copy link
Contributor

There hasn't been much activity lately and the patch still needs rebase. What is the status here?
  • Is it still relevant? ➡️ Please solve the conflicts to make it ready for review and to ensure the CI passes.
  • Is it no longer relevant? ➡️ Please close.
  • Did the author lose interest or time to work on this? ➡️ Please close it and mark it 'Up for grabs' with the label, so that it can be picked up in the future.

@fanquake
Copy link
Member

fanquake commented Dec 6, 2022

I'm going to convert this PR to draft because I think there's a better way to do this that doesn't require a separate list. I'll also pick up your other suggestions.

Given it's been 16 months this statement, I'm going to close this for now. Feel free to comment / ping if you're going to work on this again and need the PR reopened.

@fanquake fanquake closed this Dec 6, 2022
@bitcoin bitcoin locked and limited conversation to collaborators Dec 6, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants