Skip to content

fs/config: only run --password-command once when using --daemon - #9872

Merged
ncw merged 1 commit into
rclone:masterfrom
hsdfat:fix-password-command-daemon
Sep 8, 2026
Merged

ncw merged 1 commit into
rclone:masterfrom
hsdfat:fix-password-command-daemon

Conversation

@hsdfat

@hsdfat hsdfat commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

What does this change do?

With --daemon, --password-command is run twice, so a command that needs an authentication — the issue's example
is pass show, which wants a hardware key touch — asks for it twice.

SetConfigPassword saves the obscured key to the temp file named by _RCLONE_CONFIG_KEY_FILE so the daemon process
can pick it up. But the process that wrote that file then read and deleted it itself, on the very next pass through
Decrypt's loop, before it daemonized. The daemon started with the variable pointing at a file that was already
gone, found no key, and ran the password command a second time.

Two guards fix it, both restoring behaviour that is already documented or already implemented elsewhere:

  • Don't acquire a password when _RCLONE_CONFIG_KEY_FILE is set. The PassConfigKeyForDaemonization doc comment
    already says "If _RCLONE_CONFIG_KEY_FILE is present, password prompt is skipped and RCLONE_CONFIG_PASS
    ignored"
    — the code just did not honour it for the pre-loop password acquisition.
  • Only consume the key file in a process that has no key of its own, so a process that wrote one for its child
    leaves it in place.

This makes --password-command behave the way typing the password manually already does. On the manual path
getConfigPassword is called from inside the loop, after the _RCLONE_CONFIG_KEY_FILE check, so the parent never
reads back the file it just wrote — which matches @ncw's comment on the issue that the manual case works and there is
already a mechanism for this.

One consequence worth naming: now that the parent no longer consumes its own key file, a temp file holding the
obscured key survives if the daemon child never starts. That is already what the manual-password path does today, so
it is not a new class of exposure, but it is a real difference from the previous --password-command behaviour.

Testing

TestConfigLoadEncryptedWithPassCommandAndDaemon walks the handoff in one process: it loads the config with
PassConfigKeyForDaemonization set, asserts the key file still exists afterwards, then clears the key to simulate
the daemon starting and loads again with --password-command changed to return the wrong password. The second
load can only succeed by using the key file, and the file must be gone afterwards.

I checked the test pins both guards rather than merely passing, by mutating them one at a time:

  • dropping the pre-loop _RCLONE_CONFIG_KEY_FILE guard → the simulated daemon re-runs the command, gets the wrong
    password and fails to decrypt;
  • dropping the len(configKey) == 0 guard on the key-file read → the parent deletes its own key file and the first
    assertion fails.

go build ./..., make quicktest and golangci-lint run ./fs/config/... all pass.

No documentation change: the intended behaviour is already described in the PassConfigKeyForDaemonization comment.

Linked issue

Fixes #7341

Checklist

  • This change is trivial OR it has been discussed and agreed in the linked issue.
  • I have read the contribution guidelines.
  • (If I used AI tools to help write this code) I have read and understood the AI-assisted contributions guidance, and I have tested and take ownership of this change myself.
  • I have added tests for all changes in this PR if appropriate.
  • I have added documentation for the changes if appropriate. (No doc change needed — see above.)
  • All commit messages are in house style.
  • (Backend changes only) test_all passes for this backend — not a backend change.
  • This Pull Request is ready for review.

Decrypting the config with --daemon runs --password-command twice, which
means two authentications when the command needs one, such as a hardware
key touch for `pass show`.

SetConfigPassword saves the obscured key to the temp file named by
_RCLONE_CONFIG_KEY_FILE so the daemon process can pick it up, but the
process that wrote it then read and deleted that file itself before
daemonizing. The daemon started with the variable pointing at a file that
was already gone, found no key, and ran the password command again.

Skip acquiring a password when _RCLONE_CONFIG_KEY_FILE is set, as the
PassConfigKeyForDaemonization documentation already describes, and only
consume the key file in a process that has no key of its own. The parent
then leaves the key for the daemon, and the daemon uses it.

Fixes rclone#7341
@hsdfat
hsdfat requested a review from ncw as a code owner September 6, 2026 02:41

@ncw ncw left a comment

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.

Thank you :-)

One thing you noted that I agree is worth a follow-up: if the parent fails after loading the config but before the daemon starts (say the remote does not exist), the temp key file is left behind. That was already true for the interactive prompt, so no regression, but removing it from the parent at exit would tidy that up if you felt like a second PR.

@ncw
ncw merged commit 03783be into rclone:master Sep 8, 2026
8 of 9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

--password-command is run twice when using --daemon

2 participants