This argument is similar to -rpcauth but takes the value from the specified file content and can be specified from the command line.
Fixes #20387.
How is this different from includeconf with the rpcauth in it as the only content?
@MarcoFalke I thought about suggesting that but main issue is that -includeconf can't be specified in the command line. Also the file pointed by -includeconf can have more configurations.
Maybe @ruimarinho and @NicolasDorier can weight on the the approach.
This argument is similar to -rpcauth but takes the value from the
specified file content
To be used with -rpcauthfile
255 | {
256 | LogPrintf("Using rpcauth authentication.\n");
257 | + for (std::string rpcauth : gArgs.GetArgs("-rpcauth")) {
258 | + std::vector<std::string> fields;
259 | + boost::split(fields, rpcauth, boost::is_any_of(":$"));
260 | + if (fields.size() == 3) g_rpcauth.push_back(fields);
I guess it's a preexisting problem, but lack of any error feedback when the syntax is wrong seems like a usability nightmare
Yeah it is, can be improved.
Actually I think I'll do it in this branch, -rpcauth values are now processed once.
Such an -rpccookiegenerate=false option is what I had in mind in #20387. I would prefer that exactly for the simplicity of using an arbitrary string as cookie (which I can share via Docker's secret mechanism).
Syntax isn't necessarily wrong. It could be a 4th field added (eg, Knots uses it to restrict access to the named wallet).
A debug log line can't hurt though.
This seems like a reasonable option to add.
Allowing -includeconf to be specified on the command line would also be reasonable way to help address #20387.
However, both of these approaches seem more cumbersome to use and implement than the -norpccookiegenerate option suggested #20387 (comment).
It seems like that option could be implemented with simple logic like:
bool have_cookie = GetBoolArg("-rpccookiegenerate", true) ? GenerateAuthCookie(&cookie) : GetAuthCookie(&cookie);
and unlike the rpcauth value, the cookie value would be automatically picked up by bitcoin-cli client and potentially the electrs client. There wouldn't be a need to generate username, password, and hashed auth strings, pass the hashed auth string to the server, and pass the username and password strings to each client separately. The cookie value could be any random string with no need for hashing at all and placed in a common location where it is available to the server and every client that needs to read it.
Your approach doesn't support multiple credentials. I also see the cookie as a local authentication mechanism.
Kind of meh on adding yet another RPC authentication method that's only a slight variation from the other ones, but as long as it (and all combinatorial options it opens up) are tested sufficiently I'm not strongly against it either.
@laanwj while @ryanofsky approach is indeed simple (just misses the detail of not erasing cookie file on shutdown) it doesn't support different users with different whitelists. Also note that @ryanofsky approach changes cookie semantic - the cookie is available while the daemon is running. I'd even limit cookie authentication to loopback interface.
the cookie value would be automatically picked up by
bitcoin-cliclient and potentially theelectrsclient. @ryanofsky executingbitcoin-clion the bitcoind container will always works - cookie is there. And forelectrsit might be easier to just use config `cookie=USER:PASSWORD".
There wouldn't be a need to generate username, password, and hashed auth strings, pass the hashed auth string to the server, and pass the username and password strings to each client separately. The cookie value could be any random string with no need for hashing at all and placed in a common location where it is available to the server and every client that needs to read it. @ryanofsky this is not really necessary. Looks like we should just add support for bearer authentication in addition to basic authentication. In this case we could support
-rpcauth=user:tokenand-rpcauthfile=user:pathand then clients could just sendAuthorization: Bearer <token>header (the server would be able to identify the user and check its whitelist).
It just sounds reasonable to me to support different credentials and whitelists and make this easy to setup with docker containers and secrets.
@promag sure, and thanks for implementing this. I just suggested the -rpccookiegenerate boolean option because it seemed to directly address the reported issue, because it seemed simple to implement as a ~one-line change, and because it seemed like a simpler configuration to set up: not requiring any scripting or hashing, not requiring any client configuration if using bitcoin-cli or using the default cookie path with another client.
Also, it's been said a few times that -norpccookiegenerate wouldn't support multiple credentials, but this would be a easy restriction to lift now or in the future by replacing GetArg("-rpccookiefile") with GetArgs("-rpccookiefile") and std::string with std::set<std::string> a few places.
I do agree with you that it would probably make sense to stop allowing cookie authentication from non-local connections by default. I also think the -rpcauthfile option implemented here would be a useful thing to have (though allowing -includeconf on the command line could be more flexible). I can see how there might be security concerns and potential for insecure misconfiguration with cookie files since the secret isn't hashed. But I think all the solutions that have been discussed have been reasonable.
I'd say the advantage of -rpcauthfile is to have a file with just the token and so more suitable as a shared secret.
I do agree with you that it would probably make sense to stop allowing cookie authentication from non-local connections by default.
Ok, this can be submitted on a separate branch.
265 | + if (!file.is_open()) continue; 266 | + std::string rpcauth; 267 | + std::getline(file, rpcauth); 268 | + std::vector<std::string> fields; 269 | + boost::split(fields, rpcauth, boost::is_any_of(":$")); 270 | + if (fields.size() == 3) g_rpcauth.push_back(fields);
In commit "rpc: Support -rpcauthfile argument" (10ef7321f0dab8cb7dfe79f7c0f9d886d8fe0097)
I think this should refuse to load if the rpcauthfile isn't formatted correctly. Logging and error and returning false might be sufficient to do this.
I think an invalid -rpcauth option above should also trigger an error, or at least report an error, but it's pre-existing code so not directly related
I think this should refuse to load
You mean the process should exit?
re: #20407 (review)
I think this should refuse to load
You mean the process should exit?
I would exit, but don't think it would be crazy not to exit, do think it would be crazy to silently ignore a configuration error giving no feedback. I am presuming returning false exits, but didn't check.
See above, it's not necessarily an error
43 | - print('rpcauth={0}:{1}${2}'.format(args.username, salt, password_hmac)) 44 | - print('Your password:\n{0}'.format(args.password)) 45 | + if args.output: 46 | + file = open(args.output, "x") 47 | + file.write('{0}:{1}${2}'.format(args.username, salt, password_hmac)) 48 | + print('Your password:\n{0}'.format(args.password))
In commit "rpcauth: Support storing credentials in a file" (ff5d7fa1e4c340657110436a29aecff85de6eb80)
Same line is printed in else branch, could dedup.
38 | @@ -38,9 +39,14 @@ def main(): 39 | salt = generate_salt(16) 40 | password_hmac = password_to_hmac(salt, args.password) 41 | 42 | - print('String to be appended to bitcoin.conf:') 43 | - print('rpcauth={0}:{1}${2}'.format(args.username, salt, password_hmac)) 44 | - print('Your password:\n{0}'.format(args.password)) 45 | + if args.output: 46 | + file = open(args.output, "x")
In commit "rpcauth: Support storing credentials in a file" (ff5d7fa1e4c340657110436a29aecff85de6eb80)
Doesn't really matter but
with open(args.output, "x") as file:
file.write(...)
Is slightly better style because it ensures that the file is closed immediately after the write.
Also may wish to avoid file variable name and use something like f or fp since file is a builtin function.
Mode should probably be 'a'
I was thinking it should fail if the file already exists.
Then how would you build a file with many lines?
Needs to specify encoding
file = open(args.output, "x", encoding="utf8")
Code review ACK ff5d7fa1e4c340657110436a29aecff85de6eb80
<!--e57a25ab6845829454e8d69fc972939a-->
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.
<!--174a7506f384e20aa4161008e828411d-->
Reviewers, this pull request conflicts with the following ones:
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.
249 | @@ -256,9 +250,24 @@ static bool InitRPCAuthentication() 250 | LogPrintf("Config options rpcuser and rpcpassword will soon be deprecated. Locally-run instances may remove rpcuser to use cookie-based auth, or may be replaced with rpcauth. Please see share/rpcauth for rpcauth auth generation.\n"); 251 | strRPCUserColonPass = gArgs.GetArg("-rpcuser", "") + ":" + gArgs.GetArg("-rpcpassword", ""); 252 | } 253 | - if (gArgs.GetArg("-rpcauth","") != "") 254 | + if (gArgs.GetArg("-rpcauth", "") != "" || gArgs.GetArg("-rpcauthfile", "") != "")
This breaks the ability to set -rpcauth= to void all prior -rpcauth parameters.
<!--cf906140f33d8803c4a75a2196329ecb-->
🐙 This pull request conflicts with the target branch and needs rebase.
<sub>Want to unsubscribe from rebase notifications on this pull request? Just convert this pull request to a "draft".</sub>
<!--13523179cfe9479db18ec6c5d236f789-->There hasn't been much activity lately and the patch still needs rebase. What is the status here?
This has been in draft for ~ 2 years now. So I'm going to close it. Feel free to comment / ping if you'd like this re-opend.
I'd vote for re-open. The underlying issue that bitcoind is inherently difficult to use with Docker secrets, because it can't read secrets from a file, is still present. See #20387.
There's no point re-opening, if the author isn't working on it. If that's the case, we can mark it up for grabs, and someone else can pick it up (they could also just do that now).