Skip to content

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

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

fix(connectors.local): fragile sudo handling resulting in faillocks and premature errors.#1689
Dexmachi wants to merge 11 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
temp=$(mktemp "${{TMPDIR:={0}}}/pyinfra-sudo-askpass-XXXXXXXXXXXX")
cat >"$temp"<<'__EOF__'
#!/bin/sh
if [ -f "$0.${{PPID}}.called" ]; then

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.

Doesn't making this check at the top break any retry attempts? If the first password is wrong we'll never actually retry any password provided later because the .called file will still exist?

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.

yes, that was how I was trying to make it
If the user forgot the sudo password, it keeps the retry attempt, then if the user inputs it wrong, it breaks

This was done like this in order to prevent faillocks

Any suggestion as to how to change this? I also don't quite like putting a marker, but it looks like the best solution in this scenario (python calling multiple sudo scripts)

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.

Yeah this is complicated because we want to allow retries with different passwords. Currently we'll never go over one attempt ever for the duration of the askpass file. I think that breaks the while loop entirely after the first failure?

What if we just removed the .called file after the echo error before return 1? That'd shortcut sudo retries and keep the file only for the duration of that command (I think, untested).

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.

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

Comment thread src/pyinfra/connectors/util.py
@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
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