Skip to content

fix(connectors.local): fragile sudo handling resulting in faillocks and premature errors. - #1689

Open
Dexmachi wants to merge 18 commits into
pyinfra-dev:3.xfrom
Dexmachi:sudo-handling
Open

Dexmachi wants to merge 18 commits into
pyinfra-dev:3.xfrom
Dexmachi:sudo-handling

Conversation

@Dexmachi

@Dexmachi Dexmachi commented Apr 21, 2026 •

Copy link
Copy Markdown
Contributor

Closes #1043
Closes #1688

How:

Added a {0}.{PPID}.called marker + env LC_ALL=C to sudo calls, allowing Pyinfra to keep track of sudo call attempts and to have a standardized retry catch.

the marker gets removed alongside the sudo password file, in the same call.

Why:

@Local sudo handling was fragile, requiring a specific locale and not dealing with auto sudo retries, resulting in faillocks and premature errors.

Test Changes:

Had to change test_ssh and test_util to assert "env LC_ALL=C " at the start of sudo and su calls.

Before/After:

Before(normal usage):

/opt/***-app/venv/bin/pyinfra @local playbooks/install1pkg/deploy.py
    Attempting to work in a virtualenv.
    If you encounter problems, please install pyinfra inside the virtualenv.


--> Loading config...
--> Loading inventory...
--> Connecting to hosts...
    [@local] Connected

--> Preparing operation files...
    Loading: playbooks/install1pkg/deploy.py
    [@local] Ready: playbooks/install1pkg/deploy.py

--> Detected changes:
    Operation                             Change       Conditional Change   
    server.shell (commands=['echo Hy'])   1 (@local)   -                    

    Detected changes may not include every change pyinfra will execute.
    Hidden side effects of operations may alter behaviour of future operations,
    this will be shown in the results. The remote state will always be updated
    to reflect the state defined by the input operations.

    Detected changes displayed above, skip this step with -y
                             
--> Beginning operation run...
--> Starting operation: server.shell (commands=['echo Hy'])
Sinto muito, tente novamente.
Sinto muito, tente novamente.
    [@local] sudo: 3 tentativas de senha incorreta
    [@local] Error: executed 0 commands

After(normal usage):

pyinfra @local playbooks/install1pkg/deploy.py -y
--> Loading config...
--> Loading inventory...
--> Connecting to hosts...
    [@local] Connected

--> Preparing operation files...
    Loading: playbooks/install1pkg/deploy.py
    [@local] Ready: playbooks/install1pkg/deploy.py

--> Skipping change detection
--> Beginning operation run...
--> Starting operation: server.shell (commands=['echo Hy'])
Sorry, try again.
    [@local] sudo: 1 incorrect password attempt
    [@local] sudo: no password was provided
    [@local] sudo: 1 incorrect password attempt
    [@local] Error: executed 0 commands

--> Disconnecting from hosts...
--> pyinfra error: No hosts remaining!

sudo -l
[sudo] senha para dexmachina: 
Entradas de Defaults correspondentes a dexmachina em Dionysus:

Before(ad-hoc):

/opt/***-app/venv/bin/pyinfra @local server.shell "echo Hi" -y --sudo
    Attempting to work in a virtualenv.
    If you encounter problems, please install pyinfra inside the virtualenv.


--> Loading config...
--> Loading inventory...
--> Connecting to hosts...
    [@local] Connected

--> Preparing operation func...
    [@local] Ready: shell

--> Skipping change detection
--> Beginning operation run...
--> Starting operation: server.shell (echo Hi)
    [@local] sudo: uma senha é necessária
    [@local] Error: executed 0 commands

--> Disconnecting from hosts...
--> pyinfra error: No hosts remaining!
/opt/***-app/venv/bin/pyinfra @local server.shell "echo Hi" -y --sudo "a"
    Attempting to work in a virtualenv.
    If you encounter problems, please install pyinfra inside the virtualenv.


--> Loading config...
--> Loading inventory...
--> Connecting to hosts...
    [@local] Connected

--> Preparing operation func...
--> Disconnecting from hosts...
--> An internal exception occurred:

  File "/usr/lib/python3.14/inspect.py", line 3155, in _bind
    raise TypeError('too many positional arguments') from None
TypeError: too many positional arguments

--> The full traceback has been written to pyinfra-debug.log
--> If this is unexpected please consider submitting a bug report on GitHub, for more information run `pyinfra --support`.

After(ad-hoc):

pyinfra @local server.shell "echo Hi" -y --sudo
--> Loading config...
--> Loading inventory...
--> Connecting to hosts...
    [@local] Connected

--> Preparing operation func...
    [@local] Ready: shell

--> Skipping change detection
--> Beginning operation run...
--> Starting operation: server.shell (echo Hi)
[@local] sudo password: 
    [@local] sudo: a password is required
    [@local] Error: executed 0 commands

--> Disconnecting from hosts...
--> pyinfra error: No hosts remaining!
pyinfra @local server.shell "echo Hi" -y --sudo
--> Loading config...
--> Loading inventory...
--> Connecting to hosts...
    [@local] Connected

--> Preparing operation func...
    [@local] Ready: shell

--> Skipping change detection
--> Beginning operation run...
--> Starting operation: server.shell (echo Hi)
[@local] sudo password: 
Sorry, try again.
    [@local] sudo: 1 incorrect password attempt
    [@local] sudo: no password was provided
    [@local] sudo: 1 incorrect password attempt
    [@local] Error: executed 0 commands

--> Disconnecting from hosts...
--> pyinfra error: No hosts remaining!
sudo -l
[sudo] senha para dexmachina: 
Entradas de Defaults correspondentes a dexmachina em Dionysus:
  • Pull request is based on the default branch (3.x at this time)
  • [N/A] Pull request includes tests for any new/updated operations/facts
  • Pull request includes documentation for any new/updated operations/facts
  • Tests pass (see scripts/dev-test.sh)
  • Type checking & code style passes (see scripts/dev-lint.sh)

@wowi42 wowi42 added bug Label for all kind of bugs. connectors Connector issues - builtin integrations with other tools. labels May 7, 2026
Comment thread src/pyinfra/connectors/util.py Resolved
Comment thread src/pyinfra/connectors/util.py Resolved
@Dexmachi

Dexmachi commented May 7, 2026

Copy link
Copy Markdown
Contributor Author

I'm currently fixing the merge-based errorrs, 1 sec

@Dexmachi
Dexmachi requested a review from Fizzadar May 7, 2026 12:12

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

Makes sense, I'll try to check out some possible fixes for this

Any luck with this @Dexmachi? Still keen to get this in if you are!

Heads up that 3.x has moved on a fair bit since May - askpass cache keys now include the temp dir, and we match sudo-rs' "interactive authentication is required" as well as sudo's own phrasing, so this'll need a rebase before I can review it properly (as-is it'd revert both).

I still think the marker wants removing before the exit 1 rather than sticking around for the whole connection - PIDs get recycled and we only clean up on disconnect, so a later sudo can inherit a stale marker and get handed an empty password for no reason.

One other thing I missed first time round: _ensure_su_askpass_set_for_host shares ASKPASS_COMMAND, so a su password failure will print sudo: 1 incorrect password attempt - wrong tool name, and it'll trip the sudo branch in execute_command_with_sudo_retry.

@Dexmachi

Dexmachi commented Aug 3, 2026

Copy link
Copy Markdown
Contributor Author

@Fizzadar ty for the heads up!!

I'm currently qorking on a bunch of other shtuff, and got a bit burnt out for a while, but this is definitely on my back burner!

I'll make sure to address the review and try to get it to a less hacky method!

@Dexmachi
Dexmachi requested a review from Fizzadar October 20, 2026 12:25

@wowi42 wowi42 left a comment •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I checked the sudo password flows. The new retry logic introduces a few regressions that need fixing first.

Issues

  • src/pyinfra/connectors/util.py:293: an incorrect explicitly supplied _sudo_password now enters the interactive retry loop instead of returning the command failure. Non-interactive runs can raise EOFError from getpass; this reproduces on the PR head and is failing test_e2e_ssh_sudo_password, end-to-end-test (ubuntu-22.04, ssh), and tests-done.
  • src/pyinfra/connectors/util.py:468: a cached prompted password now overrides every later explicit _sudo_password, so per-operation or changed credentials are silently ignored. The PR head generates a command with old-password in this case:
    host.connector_data["prompted_sudo_password"] = "old-password"
    make_unix_command_for_host(..., _sudo=True, _sudo_password="new-password")
  • src/pyinfra/connectors/util.py:41: a successful askpass call leaves $0.${PPID}.called behind until disconnect. If sudo later reuses that PID, its first askpass call takes the incorrect-password branch and returns no password, so the earlier stale-marker feedback is only partly resolved.

Notes

  • The .claude/skills symlink replacement and new docs/_deploy_globals.rst are unrelated to sudo handling and should be removed from this PR.

Done at PyCon FR.

@Dexmachi

Dexmachi commented Nov 4, 2026

Copy link
Copy Markdown
Contributor Author

@wowi42 ty for the review!

These were issues I honestly did not think of when designing these fixes, and this PR actually fixes an actual issue I have on my own little project so I'm glad to fix them right up.

btw, about the SKILLS and the rst, they came with a random merge, I am not really sure how they ended up here, but I did remove them, thank you for pointing this out

@Dexmachi
Dexmachi requested a review from wowi42 November 4, 2026 12:30

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Label for all kind of bugs. connectors Connector issues - builtin integrations with other tools.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fragile sudo handling for @local connector sudo: 3 incorrect password attempts --> faillock, I expect only 1 incorrect password attempt

3 participants