Skip to content

rpc: Support -rpcauthfile argument#20407

Closed
promag wants to merge 3 commits intobitcoin:masterfrom
promag:2020-11-rpcauthfile
Closed

rpc: Support -rpcauthfile argument#20407
promag wants to merge 3 commits intobitcoin:masterfrom
promag:2020-11-rpcauthfile

Conversation

@promag
Copy link
Copy Markdown
Contributor

@promag promag commented Nov 17, 2020

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.

@maflcko
Copy link
Copy Markdown
Member

maflcko commented Nov 17, 2020

How is this different from includeconf with the rpcauth in it as the only content?

@promag
Copy link
Copy Markdown
Contributor Author

promag commented Nov 17, 2020

@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
@promag promag force-pushed the 2020-11-rpcauthfile branch from 8a3b62c to ff5d7fa Compare November 17, 2020 12:56
Copy link
Copy Markdown
Contributor

@ryanofsky ryanofsky left a comment

Choose a reason for hiding this comment

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

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.

for (std::string rpcauth : gArgs.GetArgs("-rpcauth")) {
std::vector<std::string> fields;
boost::split(fields, rpcauth, boost::is_any_of(":$"));
if (fields.size() == 3) g_rpcauth.push_back(fields);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I guess it's a preexisting problem, but lack of any error feedback when the syntax is wrong seems like a usability nightmare

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yeah it is, can be improved.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Actually I think I'll do it in this branch, -rpcauth values are now processed once.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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.

@promag
Copy link
Copy Markdown
Contributor Author

promag commented Nov 17, 2020

Your approach doesn't support multiple credentials. I also see the cookie as a local authentication mechanism.

@laanwj
Copy link
Copy Markdown
Member

laanwj commented Nov 18, 2020

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.

@promag
Copy link
Copy Markdown
Contributor Author

promag commented Nov 18, 2020

@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-cli client and potentially the electrs client.

@ryanofsky executing bitcoin-cli on the bitcoind container will always works - cookie is there. And for electrs it 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:token and -rpcauthfile=user:path and then clients could just send Authorization: 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.

@ryanofsky
Copy link
Copy Markdown
Contributor

ryanofsky commented Nov 18, 2020

@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.

@promag
Copy link
Copy Markdown
Contributor Author

promag commented Nov 18, 2020

I'd say the advantage of -rpcauthfile is to have a file with just the token and so more suitable as a shared secret.

@promag
Copy link
Copy Markdown
Contributor Author

promag commented Nov 18, 2020

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.

Copy link
Copy Markdown
Contributor

@ryanofsky ryanofsky left a comment

Choose a reason for hiding this comment

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

Code review ACK ff5d7fa

std::getline(file, rpcauth);
std::vector<std::string> fields;
boost::split(fields, rpcauth, boost::is_any_of(":$"));
if (fields.size() == 3) g_rpcauth.push_back(fields);
Copy link
Copy Markdown
Contributor

@ryanofsky ryanofsky Nov 18, 2020

Choose a reason for hiding this comment

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

In commit "rpc: Support -rpcauthfile argument" (10ef732)

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

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think this should refuse to load

You mean the process should exit?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

re: #20407 (comment)

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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

See above, it's not necessarily an error

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@luke-jr please review #20461, this one should rebase with that.

if args.output:
file = open(args.output, "x")
file.write('{0}:{1}${2}'.format(args.username, salt, password_hmac))
print('Your password:\n{0}'.format(args.password))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

In commit "rpcauth: Support storing credentials in a file" (ff5d7fa)

Same line is printed in else branch, could dedup.

print('rpcauth={0}:{1}${2}'.format(args.username, salt, password_hmac))
print('Your password:\n{0}'.format(args.password))
if args.output:
file = open(args.output, "x")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

In commit "rpcauth: Support storing credentials in a file" (ff5d7fa)

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.

@DrahtBot
Copy link
Copy Markdown
Contributor

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

Conflicts

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.

print('rpcauth={0}:{1}${2}'.format(args.username, salt, password_hmac))
print('Your password:\n{0}'.format(args.password))
if args.output:
file = open(args.output, "x")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Mode should probably be 'a'

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I was thinking it should fail if the file already exists.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Then how would you build a file with many lines?

for (std::string rpcauth : gArgs.GetArgs("-rpcauth")) {
std::vector<std::string> fields;
boost::split(fields, rpcauth, boost::is_any_of(":$"));
if (fields.size() == 3) g_rpcauth.push_back(fields);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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.

std::getline(file, rpcauth);
std::vector<std::string> fields;
boost::split(fields, rpcauth, boost::is_any_of(":$"));
if (fields.size() == 3) g_rpcauth.push_back(fields);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

See above, it's not necessarily an error

@promag promag marked this pull request as draft November 25, 2020 00:09
print('rpcauth={0}:{1}${2}'.format(args.username, salt, password_hmac))
print('Your password:\n{0}'.format(args.password))
if args.output:
file = open(args.output, "x")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Needs to specify encoding

Suggested change
file = open(args.output, "x")
file = open(args.output, "x", encoding="utf8")

strRPCUserColonPass = gArgs.GetArg("-rpcuser", "") + ":" + gArgs.GetArg("-rpcpassword", "");
}
if (gArgs.GetArg("-rpcauth","") != "")
if (gArgs.GetArg("-rpcauth", "") != "" || gArgs.GetArg("-rpcauthfile", "") != "")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This breaks the ability to set -rpcauth= to void all prior -rpcauth parameters.

@DrahtBot
Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
Member

fanquake commented Dec 6, 2022

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.

@fanquake fanquake closed this Dec 6, 2022
@schildbach
Copy link
Copy Markdown
Contributor

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.

@fanquake
Copy link
Copy Markdown
Member

fanquake commented Dec 6, 2022

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).

@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.

Bitcoind to use RPC authentication cookie from file

8 participants