daemon: forbid "setconfig" command to change rpcserver settings in-flight

It is much easier to reason about the rpcserver if we don't allow changing its basic settings while it is already running. What does it mean to change the TCP port it is listening on ("rpcport") if it's already running? It is even problematic to change the rpcpassword: care needs to be taken to already update it for the current server.
(ref https://github.com/spesmilo/electrum/issues/6762)

This commit disallows changing all of the "rpc*" config variables if the daemon is already running.

---

Simultaneously, it also ensures rpc_password is always set and auth cannot be disabled.

Previously if there was a daemon running, and the user ran
`$ electrum setconfig rpcpassword ""` that would leave the RPC unauthenticated
for the current session. However next time the daemon restarted, get_rpc_credentials would see
the unset password and generate one.

I think this was the worst of both worlds:
- we did not really allow removing the rpc password, except for the current session, and
- perhaps unexpectedly, we would generate a new password on daemon restart

Instead now we explicitly make sure the RPC server can never get into a state where it does not have a password set.

Based on a report by `Zuzana Kotásková <36777@mail.vsfs.cz>`
This commit is contained in:
SomberNight
2026-03-24 17:07:26 +00:00
parent 8f21f1d744
commit 0dcef9780b
2 changed files with 16 additions and 7 deletions
+3 -3
View File
@@ -180,6 +180,7 @@ def wait_until_daemon_becomes_ready(*, config: SimpleConfig, timeout=5) -> bool:
def get_rpc_credentials(config: SimpleConfig) -> Tuple[str, str]:
rpc_user = config.RPC_USERNAME or None
rpc_password = config.RPC_PASSWORD or None
# note: we explicitly forbid empty/unset password, and will generate one now instead
if rpc_user is None or rpc_password is None:
rpc_user = 'user'
bits = 128
@@ -219,9 +220,8 @@ class AuthenticatedServer(Logger):
self._methods[name] = f
async def authenticate(self, headers):
if self.rpc_password == '':
# RPC authentication is disabled
return
if not self.rpc_password:
raise Exception('Server RPC password is unset. This should not happen.')
auth_string = headers.get('Authorization', None)
if auth_string is None:
raise AuthenticationInvalidOrMissing('CredentialsMissing')