Skip to content

ENT-14418: Added --ask-pass, --password-file and --switch-user-command options - #199

Open
nickanderson wants to merge 10 commits into
cfengine:masterfrom
nickanderson:ENT-14418
Open

ENT-14418: Added --ask-pass, --password-file and --switch-user-command options#199
nickanderson wants to merge 10 commits into
cfengine:masterfrom
nickanderson:ENT-14418

Conversation

@nickanderson

@nickanderson nickanderson commented Aug 14, 2026

Copy link
Copy Markdown
Member

cf-remote can now switch user on hosts where sudo asks for a password, prompting for it once with --ask-pass or reading it from a file with --password-file, so passwordless sudo is no longer a requirement. --switch-user-command replaces the hardcoded sudo bash -c.

@nickanderson
nickanderson requested review from SimonThalvorsen, larsewi and victormlg and removed request for victormlg August 14, 2026 16:24

@larsewi larsewi left a comment

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.

🚀

Comment thread cf_remote/aramid.py Outdated
Comment on lines +120 to +122
Anything we send this way (a password for switching user) is small enough
to fit in the pipe buffer, so writing it up front cannot block. Standard
input is left alone (inherited) when there is nothing to send.

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.

Warning Use communicate() rather than .stdin.write, .stdout.read or .stderr.read to avoid deadlocks due to any of the other OS pipe buffers filling up and blocking the child process.

Is this what you are referring to?

@nickanderson nickanderson Aug 18, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yes, that's the warning. The reasoning Claude wrote was that our payload (a password) is far smaller than the pipe buffer, so the write can't block — but that argument only holds as long as nobody sends anything bigger, so I've stopped making it. _popen() now only opens the pipe and communicate(input=...) does the writing, in cd4fa30.

The one wrinkle is that _Task.communicate() polls with a short timeout, and communicate() raises ValueError: Cannot send input after starting communication if a later call hands it the same input again. It remembers what the first call gave it and keeps writing from where it left off, so only the first call passes it (and a retried process gets a fresh one, so the flag resets). tests/test_aramid.py::test_input_is_only_handed_over_once covers that — it fails with that ValueError if the flag goes away.

Comment thread cf_remote/ssh.py Outdated
Comment thread tests/shell/002_sudo_password.sh Outdated
@nickanderson
nickanderson requested a review from larsewi August 18, 2026 17:35

@victormlg victormlg left a comment

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.

My comments

Comment thread cf_remote/ssh.py
Comment on lines +456 to +460
needs_password = (
"a terminal is required" in output
or "a password is required" in output
or "no tty present" in output
)

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.

This doesn't feel very robust to me. What if instead cf-remote would by default run sudo non-interactively sudo -n ? This would collapse all these errors into a single one: sudo: a password is required. Then you can accurately print this hint.

However, I don't know how the rest of the code would react to this change

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Taken — the default is sudo -n bash -c now, in 8fc3d4f, so the case that matters collapses to sudo: a password is required the way you described, and that is the first thing checked. The other two wordings stay for a --switch-user-command we were given, which has no -n to ask with.

The part -n doesn't fix, and you were right to poke at it: sudo says that in the caller's language, and ssh forwards LANG/LC_* by default. On Fedora 41 the same failure reads sudo: Ein Passwort ist notwendig, so the hint just went quiet. 75fa7b2 pins LC_ALL=C on the command, which makes the wording we match the one that arrives, with a case in tests/shell/002_sudo_password.sh that fails if the pin goes away.

Comment thread cf_remote/ssh.py
Comment on lines +125 to +136
def needs_password_on(self, connection):
"""Check whether switching user on this host requires a password

Only interesting when we actually have a password to send. Sending it
when it isn't needed would leave it on the standard input of the
command we are running instead.

'sudo -n' answers this without ever attempting to authenticate. Asking
by letting an attempt fail instead would count towards the failed
attempts that pam_faillock locks accounts out over, once per host and
run.
"""

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.

Maybe this wouldn't be needed if by default we run sudo -n ? (see other comment)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Taken the other two: the default is now sudo -n bash -c, and SwitchUser is built inside run_command_with_args instead of being passed in.

-n and -S can't be combined. -n means never prompt, so sudo -n -S refuses the password. When there is a password the command is still sudo -S -p '' bash -c.

needs_password_on() decides whether the password is written to the child's stdin at all. On a NOPASSWD host, sending it puts it on the stdin of the wrapped command instead of into sudo. (tests/shell/002_sudo_password.sh).

Comment thread cf_remote/main.py Outdated
nickanderson and others added 9 commits September 2, 2026 15:17
Ticket: ENT-14418
Changelog: title
Signed-off-by: Nick Anderson <nick@cmdln.org>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Ticket: ENT-14418
Changelog: title
Signed-off-by: Nick Anderson <nick@cmdln.org>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Ticket: ENT-14418
Changelog: none
Signed-off-by: Nick Anderson <nick@cmdln.org>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Ticket: ENT-14418
Changelog: none
Signed-off-by: Nick Anderson <nick@cmdln.org>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…bals

Ticket: ENT-14418
Changelog: none
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Nick Anderson <nick@cmdln.org>
Ticket: ENT-14418
Changelog: none
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Nick Anderson <nick@cmdln.org>
Ticket: ENT-14418
Changelog: none
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Nick Anderson <nick@cmdln.org>
Ticket: ENT-14418
Changelog: none
Signed-off-by: Nick Anderson <nick@cmdln.org>
Ticket: ENT-14418
Changelog: none
Signed-off-by: Nick Anderson <nick@cmdln.org>
@codecov

codecov Bot commented Sep 2, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 86.61972% with 19 lines in your changes missing coverage. Please review.
✅ Project coverage is 47.53%. Comparing base (40dc552) to head (75fa7b2).

Files with missing lines Patch % Lines
cf_remote/commands.py 61.11% 7 Missing ⚠️
cf_remote/main.py 68.75% 5 Missing ⚠️
cf_remote/ssh.py 95.18% 4 Missing ⚠️
cf_remote/aramid.py 83.33% 2 Missing ⚠️
cf_remote/remote.py 83.33% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #199      +/-   ##
==========================================
+ Coverage   44.27%   47.53%   +3.25%     
==========================================
  Files          18       18              
  Lines        3065     3158      +93     
==========================================
+ Hits         1357     1501     +144     
+ Misses       1708     1657      -51     
Files with missing lines Coverage Δ
cf_remote/args.py 100.00% <100.00%> (ø)
cf_remote/remote.py 30.90% <83.33%> (+0.44%) ⬆️
cf_remote/aramid.py 66.20% <83.33%> (+2.08%) ⬆️
cf_remote/ssh.py 82.50% <95.18%> (+8.09%) ⬆️
cf_remote/main.py 46.53% <68.75%> (+7.38%) ⬆️
cf_remote/commands.py 18.99% <61.11%> (+3.64%) ⬆️

... and 3 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Ticket: ENT-14418
Changelog: none
Signed-off-by: Nick Anderson <nick@cmdln.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants