homebrew-autoupdate icon indicating copy to clipboard operation
homebrew-autoupdate copied to clipboard

Handling Casks

Open DomT4 opened this issue 3 years ago • 16 comments

Issue:

  • Currently Casks that use sudo to upgrade can block this command from executing successfully when people use autoupdate to execute upgrade on their behalf, because of the hang on silently waiting for sudo authorisation.
  • Homebrew explicitly does not support launching brew upgrade with sudo, immediately bailing on the command, meaning we can't execute the Cask upgrade command as root to work around the issue.

Potential Fix:

  • Using Homebrew's API to work out which Casks are outdated, and which of those Casks need sudo usage to upgrade, only passing those non-sudo Casks to the upgrade command.

Problems with Fix:

  • Likely to be computationally expensive, making the command very slow to execute.
  • Leaves sudo Casks behind intentionally; would need to find a way to gracefully notify the user to their presence & reason for not upgrading.
  • Leaning on Homebrew's API so heavily to work out what casks are outdated and which of those need sudo has a very real potential to be fragile and prone to breaking.
  • Quite a lot of work for something I'm not sure how widely is a use-case yet.

Hacky Temporary Workaround

  • Use SUDO_ASKPASS as Homebrew seems to intentionally support that for non-interactive installs, and works when tested locally.
  • I don't really want to settle on that as a long-term solution because I'm a tad uncomfortable asking people to simply leave their sudo passwords sat on the filesystem unguarded in plaintext.

Related Issues:

  • https://github.com/DomT4/homebrew-autoupdate/issues/35

DomT4 avatar Feb 26 '21 01:02 DomT4

Maybe it is possible to make a custom brew-autoupdate user with special sudo rights that can only install casks?

whazor avatar Mar 22 '21 08:03 whazor

Follow multi-user homebrew guide: https://medium.com/@leifhanack/homebrew-multi-user-setup-e10cb5849d59 Running autoupdate service under a different use: https://apple.stackexchange.com/a/93174 Allowing sudo without password: add file under /private/etc/sudoers.d/brew-sudo with brew ALL=(ALL) NOPASSWD: ALL

whazor avatar Mar 22 '21 09:03 whazor

Use SUDO_ASKPASS as Homebrew seems to intentionally support that for non-interactive installs, and works when tested locally.

Actually I had also encountered such problem when implementing my own auto-updater for Mac, MacDaily. So basically, my solution was to temporarily set SUDO_ASKPASS to a tailored AppleScript program at runtime to request sudo password on the fly.

My implementation for the AppleScript is at macdaily/res/askpass.applescript, which was originally inspired from another Homebrew formula called ssh-askpass.

JarryShaw avatar Apr 18 '21 14:04 JarryShaw

Out of curiosity @JarryShaw , how hard was it to implement? Tempted to look further into it and attempt it myself, especially if no one else has got around to it yet :)

CiaronHowell avatar Jul 30 '21 13:07 CiaronHowell

Hacky Temporary Workaround

  • Use SUDO_ASKPASS as Homebrew seems to intentionally support that for non-interactive installs, and works when tested locally.
  • I don't really want to settle on that as a long-term solution because I'm a tad uncomfortable asking people to simply leave their sudo passwords sat on the filesystem unguarded in plaintext.

Setting NOPASSWD in /etc/sudoers might be a better alternative.

marlonrichert avatar Aug 02 '21 18:08 marlonrichert

Out of curiosity @JarryShaw , how hard was it to implement? Tempted to look further into it and attempt it myself, especially if no one else has got around to it yet :)

@CiaronHowell It was quite a simple task with some tests lol as you may have known, AppleScript is never well documented but still easy to understand...

Setting NOPASSWD in /etc/sudoers might be a better alternative.

I don't quite like this idea... 🔗 and I suppose doing so may require either user manually editing their /etc/sudoers or the Command itself modifying the file when installation -- seems not very brew-ish

JarryShaw avatar Aug 05 '21 13:08 JarryShaw

NOPASSWD should be acceptable as you would only allow the auto update command to be executed. Installation should be a manual action to make the user aware, auto updating with sudo rights is inherently a security risk. That is also why I would understand for brew not to support this. However I hope it will exist anyway, perhaps unofficially.

whazor avatar Aug 05 '21 13:08 whazor

@CiaronHowell It was quite a simple task with some tests lol as you may have known, AppleScript is never well documented but still easy to understand...

Thank you, I'll give it a go soon 😄

CiaronHowell avatar Aug 06 '21 15:08 CiaronHowell

I use PAM for sudo. The issue I encounter is that when autoupdater spawns a new process with sudo, the prompt does not include any useful information about the command which supposed to run. As a Linux user, MacOS messages are not useful at all. I would like to know what exactly sudo tries to run, not just that it wants to escalate permissions. If we were able to change this behavior, PAM authentication would be a perfect solution IMO. image

regulskimichal avatar Oct 22 '21 16:10 regulskimichal

I made a one-liner script to store my password in the keychain following this guide. It should be a lot more secure than storing your password in plaintext.

Omoeba avatar Nov 15 '21 18:11 Omoeba

I made a one-liner script to store my password in the keychain following this guide. It should be a lot more secure than storing your password in plaintext.

Could you share details of how you use that script?

regulskimichal avatar Nov 16 '21 13:11 regulskimichal

I made a one-liner script to store my password in the keychain following this guide. It should be a lot more secure than storing your password in plaintext.

Could you share details of how you use that script?

I added export SUDO_ASKPASS="/Users/$(whoami)/getpass.sh" to "/Users/$(whoami)/Library/Application Support/com.github.domt4.homebrew-autoupdate/brew_autoupdate". The script is named getpass.sh.

Omoeba avatar Nov 17 '21 22:11 Omoeba

My implementation for the AppleScript is at macdaily/res/askpass.applescript, which was originally inspired from another Homebrew formula called ssh-askpass.

This could be a potential solution if we're able to capture which cask requires sudo privilege

NikSWE avatar Nov 18 '21 15:11 NikSWE

I made a one-liner script to store my password in the keychain following this guide. It should be a lot more secure than storing your password in plaintext.

note that the script in the guide must be slightly modified, since SUDO_ASKPASS expects a script that spits just your password to standard out when invoked.

Here's what my getpass.sh contained:

pw_name="CLI"
pw_account="matt"

if ! cli_password=$(security find-generic-password -w -s "$pw_name" -a "$pw_account"); then
  echo "error $?"
  exit 1
fi

echo "$cli_password"

SwiftWinds avatar May 29 '22 01:05 SwiftWinds

I use PAM for sudo .. the prompt does not include any useful information about the command which supposed to run

I was going to suggest this, but you beat me to to it! I check which process is requesting sudo access with sudo procs --tree sudo. I'm sure there's an equivalent incantation using ps. And yes, I see the irony of using sudo to check where sudo is being invoked.

hybras avatar Jul 31 '22 15:07 hybras

I wonder if pinentry could be used via SUDO_ASKPASS? I believe it doesn't require a terminal for password input as it uses its own gui prompt. This is what the gpgagent daemon uses (or can be setup to use).

tmillr avatar Oct 11 '22 21:10 tmillr

TL;DR solution (using getpass.sh)

(assuming your username is "mateowang" and password is "password123")

  1. security add-generic-password -s 'CLI' -a 'mateowang' -w 'password123' -T /usr/bin/security 1
  2. run security find-generic-password -w -s 'CLI' -a 'mateowang' and confirm that it outputs 'password123' 1
  3. write:
    pw_name="CLI"
    pw_account="mateowang"
    
    if ! cli_password=$(security find-generic-password -w -s "$pw_name" -a "$pw_account"); then
      echo "error $?"
      exit 1
    fi
    
    echo "$cli_password"
    
    to ~/getpass.sh 2
  4. chmod +x ~/getpass.sh
  5. add export SUDO_ASKPASS="/Users/$(whoami)/getpass.sh" to "/Users/$(whoami)/Library/Application Support/com.github.domt4.homebrew-autoupdate/brew_autoupdate". (Note: you may have to :w! if you're using vim as it is a read-only file.) 3

SwiftWinds avatar Jun 13 '23 21:06 SwiftWinds

TL;DR solution (using getpass.sh)

(assuming your username is "mateowang" and password is "password123")

  1. security add-generic-password -s 'CLI' -a 'mateowang' -w 'password123' -T /usr/bin/security 1

  2. run security find-generic-password -w -s 'CLI' -a 'mateowang' and confirm that it outputs 'password123' 1

  3. write:

    pw_name="CLI"
    pw_account="mateowang"
    
    if ! cli_password=$(security find-generic-password -w -s "$pw_name" -a "$pw_account"); then
      echo "error $?"
      exit 1
    fi
    
    echo "$cli_password"
    

    to ~/getpass.sh 2

  4. chmod +x ~/getpass.sh

  5. add export SUDO_ASKPASS="/Users/$(whoami)/getpass.sh" to "/Users/$(whoami)/Library/Application Support/com.github.domt4.homebrew-autoupdate/brew_autoupdate". (Note: you may have to :w! if you're using vim as it is a read-only file.) 3

Any security risks when using this?

benwaco avatar Jun 17 '23 04:06 benwaco

Setting NOPASSWD in /etc/sudoers might be a better alternative.

I don't quite like this idea... 🔗 and I suppose doing so may require either user manually editing their /etc/sudoers or the Command itself modifying the file when installation -- seems not very brew-ish

You don't need to modify /etc/sudoers, just create a new file in /private/etc/sudoers.d

conuaunoc avatar Aug 03 '23 21:08 conuaunoc

I wonder if pinentry could be used via SUDO_ASKPASS? I believe it doesn't require a terminal for password input as it uses its own gui prompt. This is what the gpgagent daemon uses (or can be setup to use).

Yes, it can. https://github.com/milanvarady/Applite/issues/5#issuecomment-1692672608

toobuntu avatar Aug 29 '23 01:08 toobuntu

I wonder if pinentry could be used via SUDO_ASKPASS? I believe it doesn't require a terminal for password input as it uses its own gui prompt. This is what the gpgagent daemon uses (or can be setup to use).

Yes, it works and is in my opinion the most secure and user-friendly solution.

Setup

Dependency

brew install pinentry-mac

Create Password Script

nano $HOME/getpass.sh

getpass.sh:

#!/bin/bash
PW="$(printf "%s\n" "SETOK OK" "SETCANCEL Cancel" "SETDESC homebrew-autoupdate needs your admin password to complete the task" "SETPROMPT Enter Password:$
echo "$PW"

Invoke Password Script when homebrew-autoupdate runs

sudo nano "/Users/$(whoami)/Library/Application Support/com.github.domt4.homebrew-autoupdate/brew_autoupdate"

add: export SUDO_ASKPASS=${HOME}/getpass.sh before the last line.

Test

Test Password Dialog

export SUDO_ASKPASS=${HOME}/getpass.sh

sudo -i -A

Screenshot 2023-10-25 at 09 33 18

Test homebrew-autoupdate

launchctl start com.github.domt4.homebrew-autoupdate

tail -f ~/Library/Logs/com.github.domt4.homebrew-autoupdate/com.github.domt4.homebrew-autoupdate.out

swissbuechi avatar Oct 25 '23 07:10 swissbuechi

I wonder if pinentry could be used via SUDO_ASKPASS? I believe it doesn't require a terminal for password input as it uses its own gui prompt. This is what the gpgagent daemon uses (or can be setup to use).

Yes, it works and is in my opinion the most secure and user-friendly solution.

Setup

Dependency

brew install pinentry-mac

Create Password Script

nano $HOME/getpass.sh

getpass.sh:

#!/bin/bash
PW="$(printf "%s\n" "SETOK OK" "SETCANCEL Cancel" "SETDESC homebrew-autoupdate needs your admin password to complete the task" "SETPROMPT Enter Password:$
echo "$PW"

Invoke Password Script when homebrew-autoupdate runs

sudo nano "/Users/$(whoami)/Library/Application Support/com.github.domt4.homebrew-autoupdate/brew_autoupdate"

add: export SUDO_ASKPASS=${HOME}/getpass.sh before the last line.

Test

Test Password Dialog

export SUDO_ASKPASS=${HOME}/getpass.sh

sudo -i -A

Screenshot 2023-10-25 at 09 33 18 ### Test homebrew-autoupdate `launchctl start com.github.domt4.homebrew-autoupdate `

tail -f ~/Library/Logs/com.github.domt4.homebrew-autoupdate/com.github.domt4.homebrew-autoupdate.out

I guess you could also use TouchID: https://github.com/jorgelbg/pinentry-touchid

mietzen avatar Oct 25 '23 10:10 mietzen

I wonder if pinentry could be used via SUDO_ASKPASS? I believe it doesn't require a terminal for password input as it uses its own gui prompt. This is what the gpgagent daemon uses (or can be setup to use).

Yes, it works and is in my opinion the most secure and user-friendly solution.

Setup

Dependency

brew install pinentry-mac

Create Password Script

nano $HOME/getpass.sh getpass.sh:

#!/bin/bash
PW="$(printf "%s\n" "SETOK OK" "SETCANCEL Cancel" "SETDESC homebrew-autoupdate needs your admin password to complete the task" "SETPROMPT Enter Password:$
echo "$PW"

Invoke Password Script when homebrew-autoupdate runs

sudo nano "/Users/$(whoami)/Library/Application Support/com.github.domt4.homebrew-autoupdate/brew_autoupdate" add: export SUDO_ASKPASS=${HOME}/getpass.sh before the last line.

Test

Test Password Dialog

export SUDO_ASKPASS=${HOME}/getpass.sh sudo -i -A Screenshot 2023-10-25 at 09 33 18

Test homebrew-autoupdate

launchctl start com.github.domt4.homebrew-autoupdate tail -f ~/Library/Logs/com.github.domt4.homebrew-autoupdate/com.github.domt4.homebrew-autoupdate.out

I guess you could also use TouchID: https://github.com/jorgelbg/pinentry-touchid

It would be awesome if we could use the touch ID. But I don't think it's as simple as using pinetry-mac. From where would pinetry-touchid receive the sudo password? Maybe you could manually store the password in the keyring and access the secret via pinetry-touchid. Also it would not work for older macs without touch ID.

swissbuechi avatar Oct 25 '23 12:10 swissbuechi

The argument --sudo from PR https://github.com/Homebrew/homebrew-autoupdate/pull/110 did solve this issue.

@DomT4 I believe you can now close?

swissbuechi avatar Nov 11 '23 12:11 swissbuechi

The argument --sudo from PR #110 did solve this issue.

@swissbuechi Thank you for this very useful addition to Homebrew Autoupdate!

Out of curiosity, why do you invoke a subshell and then echo the password, when it works as well to not do that?

toobuntu avatar Nov 12 '23 00:11 toobuntu

@toobuntu

Thank you for this very useful addition to Homebrew Autoupdate!

I'm glad you like it 👍🏻

Out of curiosity, why do you invoke a subshell and then echo the password, when it works as well to not do that?

The `brew autoupdate start --sudo` command does generate the content of a script, that will invoke pinentry and echo out the pw. The command will then set the `SUDO_ASKPASS` environment variable to the path of this script. Every command which supports `SUDO_ASKPASS` will now invoke this script automatically.

EDIT:

@toobuntu was right, I did not understand his initial question about invoking a subshell, he already fixed it in PR: https://github.com/Homebrew/homebrew-autoupdate/pull/128

swissbuechi avatar Nov 12 '23 01:11 swissbuechi

@DomT4 I think this issue could now be closed.

swissbuechi avatar Dec 29 '23 12:12 swissbuechi