Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to enable sudo authentication with Touch ID #228

Merged
merged 5 commits into from
Sep 20, 2022

Conversation

malob
Copy link
Contributor

@malob malob commented Sep 11, 2020

There's a one line addition to /etc/pam.d/sudo that allows Macs with Touch ID to use Touch ID to authenticate sudo requests. It's a really nice thing to have. Seem like a nice option to add to nix-darwin.

I wasn't quite sure where to put the option, so definitely feel free to ask me to make changes to put it somewhere that makes more sense.

I choose to use system.patch rather than environment.etc since I didn't want to require the user to have to delete the existing file potentially breaking sudo authentication.

@malob malob force-pushed the sudo-touchid branch 2 times, most recently from 0fa9f5b to 1f2345c Compare September 11, 2020 19:47
@jrolfs
Copy link
Contributor

jrolfs commented Sep 11, 2020

Does this work outside of Terminal.app??

@malob
Copy link
Contributor Author

malob commented Sep 12, 2020

Yes. I use it with Kitty terminal.


{
options = {
system.sudo.touchid.enable = mkEnableOption "Enable sudo authentication with Touch ID";
Copy link
Owner

Choose a reason for hiding this comment

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

NixOS uses security.sudo.* for options that manage sudoers, etc. Also this is more a pam option.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point. Looking at NixOS options that exist, I'm now thinking that something like security.pam.enableSudoTouchIdAuth would be better. What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Pushed a new commit moving that that name for now.

modules/system/sudo.nix Outdated Show resolved Hide resolved
@malob malob force-pushed the sudo-touchid branch 2 times, most recently from ce00b53 to 2e2a6e0 Compare September 14, 2020 20:43
Comment on lines 18 to 37
mkSudoTouchIdAuthScript = isEnabled:
let
file = "/etc/pam.d/sudo";
option = "security.pam.enableSudoTouchIdAuth";
in ''
${if isEnabled then ''
# Enable sudo Touch ID authentication, if not already enabled
if ! grep 'pam_tid.so' ${file} > /dev/null; then
sed -i "" '2i\
auth sufficient pam_tid.so # nix-darwin: ${option}
' ${file}
fi
'' else ''
# Disable sudo Touch ID authentication, if added by nix-darwin
if grep '${option}' ${file} > /dev/null; then
sed -i "" '/${option}/d' ${file}
fi
''}
'';
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@emilazy, here's my go at moving the activation script to using sed for both enable and disable this functionality, including experimenting with adding the options name in a comment as a unique identifier to make removing the relevant line a little more robust.

@malob malob requested a review from LnL7 October 5, 2020 18:05
@malob
Copy link
Contributor Author

malob commented Dec 3, 2020

@LnL7, just wanted to check in and see if there was anything I could do to help get this merged in.

@ethancedwards8
Copy link

I would also love for this to get merged as soon as possible! @LnL7

@nathanshelly nathanshelly mentioned this pull request Jan 15, 2021
16 tasks
modules/security/pam.nix Outdated Show resolved Hide resolved
@emilazy
Copy link
Collaborator

emilazy commented Mar 23, 2021

Gentle ping. Would love to see this merged so I don’t have to sudo -e /etc/pam.d/sudo every macOS upgrade.

montchr added a commit to montchr/dotfield that referenced this pull request May 10, 2021
@ivankovnatsky
Copy link

this looks like ready and tested, any reason not to merge it?

@ivankovnatsky
Copy link

maybe not the best solution, but added pam module to fix touch id under tmux: ivankovnatsky/nixos-config@6b3c746

@rubenfonseca
Copy link

Why not merge this?

@dhess
Copy link
Contributor

dhess commented Jul 31, 2021

FYI, you should change the use of sed in your script to /usr/bin/sed. If the user has a sed installed from Nixpkgs, it might not work with the syntax you're using.

@shinzui
Copy link

shinzui commented Dec 16, 2021

Can this option also handle pam_reattach which is also required to make sudo work in tmux?

@shinzui
Copy link

shinzui commented Dec 16, 2021

Here is an example config that uses both.

@malob
Copy link
Contributor Author

malob commented Dec 23, 2021

@shinzui, I could imagine this module being updated to support this. Though I’d rather focus on getting the initial functionality merged before adding more complexity.

Speaking of which, @LnL7, anything I can do/change to get this merged?

@dsyang
Copy link
Contributor

dsyang commented Dec 25, 2021

question from a newbie to nix and nix-darwin: When I add this, it works beautifully. However, if I do a darwin-rebuild --rollback, it appears this doesn't delete the added line from my /etc/pam.d/sudo file. I have to do security.pam.enableSudoTouchIdAuth = false and rebuild before it's removed.

Is this expected behavior?

@ethancedwards8
Copy link

Can reproduce. Simply removing the option does not remove the addition, you have to explicitly set it to false. This probably is not expected behavior.

@malob
Copy link
Contributor Author

malob commented Jan 12, 2022

@dsyang, @ethancedwards8, huh that is surprising to me, you are right that this is not the expected behavior, though I'm not sure what the cause would be.

I tested this on my system (where I've got a copy of the module setup), and was not able to reproduce. Pretty sure the only difference between the two is that in the copy in my repo I'm setting system.activationScripts.extraActivation.text instead of system.activationScripts.pam.text so that I don't have to maintain an up to date copy of modules/system/activation-scripts.nix.

I don't see why that would change anything though. The security.pam.enableSudoTouchIdAuth option defaults to false so I don't know why not specifying the option as opposed to explicitly setting it to false would make a difference.

Any ideas?

@malob
Copy link
Contributor Author

malob commented Jan 12, 2022

(Just rebased on master.)

@malob
Copy link
Contributor Author

malob commented Jan 12, 2022

I just tested this module directly by changing the nix-darwin flake in my config to be github:malob/nix-darwin/sudo-touchid and still wasn't able to reproduce.

With the option not specified in my config nix repl shows that the option is indeed present and set to false,

nix-repl> darwinConfigurations.MaloBookProM1.config.security.pam.enableSudoTouchIdAuth
false

the activation script is set with the right content,

nix-repl> darwinConfigurations.MaloBookProM1.config.system.activationScripts.pam.text
"# PAM settings\necho >&2 \"setting up pam...\"\n# Disable sudo Touch ID authentication, if added by nix-darwin\nif grep 'security.pam.enableSudoTouchIdAuth' /etc/pam.d/sudo > /dev/null; then\n  sed -i \"\" '/security.pam.enableSudoTouchIdAuth/d' /etc/pam.d/sudo\nfi\n\n\n"

and after running a rebuild and switch, the relevant line from /etc/pam.d/sudo was removed:

❯ cat /etc/pam.d/sudo
# sudo: auth account password session
auth       sufficient     pam_smartcard.so
auth       required       pam_opendirectory.so
account    required       pam_permit.so
password   required       pam_deny.so
session    required       pam_permit.so

@dsyang
Copy link
Contributor

dsyang commented Jan 21, 2022

Hmm interesting. Could this be related to flakes? My setup did not use nix-flakes.

@dsyang
Copy link
Contributor

dsyang commented Mar 9, 2022

@malob I'm setting up a new computer today and idk what has changed but I'm not hitting the same bug anymore!

$> darwin-rebuild --list-generations  
   1   2022-03-09 10:38:35   
   2   2022-03-09 10:41:10   
   3   2022-03-09 10:45:53   
   4   2022-03-09 10:55:34   
   5   2022-03-09 11:00:02   (current)

$> git diff . 
diff --git a/darwin-configuration.nix b/darwin-configuration.nix
index ea78c2d..d1fab3c 100644
--- a/darwin-configuration.nix
+++ b/darwin-configuration.nix
@@ -88,7 +88,7 @@
   };
 
   # add touchid support to sudo
-  security.pam.enableSudoTouchIdAuth = true;
+  ### security.pam.enableSudoTouchIdAuth = true;
 
   # List packages installed in system profile. To search by name, run:
   # $ nix-env -qaP | grep wget


$> darwin-rebuild switch
building the system configuration...
trace: warning: literalExample is deprecated, use literalExpression instead, or use literalDocBook for a non-Nix description.
this derivation will be built:
  /nix/store/2rk0gyshrszriyn7dqn321p1g20sw7zk-darwin-system-22.05pre358993.3e644bd6248+darwin4.0000000.drv
building '/nix/store/2rk0gyshrszriyn7dqn321p1g20sw7zk-darwin-system-22.05pre358993.3e644bd6248+darwin4.0000000.drv'...
user defaults...
setting up user launchd services...
setting up pam...
setting up ~/Applications...
applying patches...
setting up /etc...
...

$> cat /etc/pam.d/sudo
# sudo: auth account password session
auth       sufficient     pam_smartcard.so
auth       required       pam_opendirectory.so
account    required       pam_permit.so
password   required       pam_deny.so
session    required       pam_permit.so

$> darwin-rebuild --list-generations
   1   2022-03-09 10:38:35   
   2   2022-03-09 10:41:10   
   3   2022-03-09 10:45:53   
   4   2022-03-09 10:55:34   
   5   2022-03-09 11:00:02   
   6   2022-03-09 11:05:37   (current)

$> darwin-rebuild -G 5
switching profile from version 6 to 5
user defaults...
setting up user launchd services...
setting up pam...
setting up ~/Applications...
applying patches...
setting up /etc...
...

$> cat /etc/pam.d/sudo
# sudo: auth account password session
auth       sufficient     pam_tid.so # nix-darwin: security.pam.enableSudoTouchIdAuth
auth       sufficient     pam_smartcard.so
auth       required       pam_opendirectory.so
account    required       pam_permit.so
password   required       pam_deny.so
session    required       pam_permit.so

My setup can be found at https://github.com/dsyang/nixfiles. The patch there doesn't have the new gnused changes though. Will try updating my patch next.

@dsyang
Copy link
Contributor

dsyang commented Mar 9, 2022

confirmed patching in the gnused changes still work. I'm not sure what I ran into but it's gone...

@CorbanR
Copy link

CorbanR commented Mar 29, 2022

Tested and also works great for me!

@malob
Copy link
Contributor Author

malob commented Mar 29, 2022

@domenkozar, think there's a chance we could get this merged? Any changes you'd like to see before doing so?

@lockejan
Copy link
Contributor

lockejan commented Jun 27, 2022

What about devices without touchId? 🤔
There's no mechanism to prevent the user's from shooting themself in the foot.
Maybe that's missing. Invalid modules would make Sudo completely unusable and force the user to boot into recovery mode (looking more at third party modules such as pam-reattach for tmux integration).😬

@ivankovnatsky
Copy link

ivankovnatsky commented Jun 27, 2022

What about devices without touchId? thinking There's no mechanism to prevent the user's from shooting themself in the foot. Maybe that's missing. Invalid modules would make Sudo completely unusable and force the user to boot into recovery mode.grimacing

Yes, I've stumbled on something similar when configuring a new mac (but I have a bit more complex config), so you can always enable super user and edit the sudoers file as desired to fix this.

@lockejan
Copy link
Contributor

It's probably to unsafe in it's current state. See also #358 (comment)

@emilazy
Copy link
Collaborator

emilazy commented Jun 27, 2022

I expect pam_tid would fail unconditionally on machines without Touch ID (and therefore fall back to password authentication, as it already does when Touch ID authentication fails). Have you tested this? I don't have any machines without Touch ID to verify this on, but I would be very surprised if the available PAM modules depend on the hardware configuration, given how macOS system volumes and updates work, and cancelling Touch ID authentication when it's offered already falls back to asking for a password.

@lockejan
Copy link
Contributor

I expect pam_tid would fail unconditionally on machines without Touch ID (and therefore fall back to password authentication, as it already does when Touch ID authentication fails). Have you tested this? I don't have any machines without Touch ID to verify this on, but I would be very surprised if the available PAM modules depend on the hardware configuration, given how macOS system volumes and updates work, and cancelling Touch ID authentication when it's offered already falls back to asking for a password.

You're probably right. At least that's the case on my machines, when my lid is closed and I use an external keyboard. However, it would be good to ensure the same goes for older machines not having any fingerprint sensors at all.

@maxmouchet
Copy link

For what it's worth, it is completely transparent on macOS 12 VM. It directly asks for the password.

@malob
Copy link
Contributor Author

malob commented Jun 30, 2022

(Rebased on master to address merge conflict.)

@tommyknows
Copy link

Just wanted to chime in here with a couple of points. First off, let me say that I'd love to see this merged! However, in my case I'm using tmux, which means I also need the pam_reattach module to be installed and added to the /etc/pam.d/sudo file as well.

I think building the pam_reattach module & adding this as an option would technically be possible, but definitely out of scope for this PR. And as I'm just getting started with Nix, I can't really provide a PR for this either.

But it would be nice to also see an option for "additionalLines` or something. For example, I could image this to look something like:

{
  security.pam.enableSudoTouchIdAuth = true;
  security.pam.additionalLines = [
    "auth       optional       pam_reattach.so"
  ];
}

That would be super helpful!

@DylanRJohnston-FZ
Copy link

I've extended it to handle pam_reattach https://github.com/DylanRJohnston/nixos/blob/main/common/nix-darwin/touchID.nix with pam_reattach being built as an overlay here https://github.com/DylanRJohnston/nixos/blob/main/packages/pam_reattach.nix

aarnphm added a commit to aarnphm/dix that referenced this pull request Aug 15, 2022
See LnL7/nix-darwin#228

Signed-off-by: Aaron Pham <29749331+aarnphm@users.noreply.github.com>
@Enzime
Copy link
Collaborator

Enzime commented Sep 20, 2022

@domenkozar any chance you could take a look at this PR? I've been using it and it's been working quite well for me

I've also just tested it on my M1 Mac Mini without TouchID and it falls back to password seamlessly :)

@domenkozar domenkozar merged commit b3de9dd into LnL7:master Sep 20, 2022
@malob malob deleted the sudo-touchid branch September 20, 2022 21:21
@malob
Copy link
Contributor Author

malob commented Sep 20, 2022

❤️

Comment on lines +12 to +13
# sudo. We also can't use `system.patchs` since it only runs once, and so won't patch in the
# changes again after OS updates (which remove modifications to this file).
Copy link
Contributor

Choose a reason for hiding this comment

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

What's this about system.patches? I haven't used it before but reading through its activation script the logic appears to be "apply the patch unless it's already applied" (where "already applied" is defined as "it can be successfully reversed"). The part that looks up the previously-applied patches is just to reverse any patches that have been removed.

Which is to say, it certainly appears as though it will attempt to repatch on every activation.

Copy link
Contributor

@maljub01 maljub01 Mar 7, 2023

Choose a reason for hiding this comment

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

💯

system.patches is definitely the best and safest option. It will always get reapplied unless the existing contents are "unexpected" (ie. the patches don't apply cleanly).

Copy link
Contributor

Choose a reason for hiding this comment

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

In fact, using system.patches to modify pam is something that the example config alludes to here:

# system.patches = [ ./pam.patch ];

Copy link
Contributor

Choose a reason for hiding this comment

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

It turns out, system.patches is actually broken. This PR should fix it: #612

Copy link
Contributor

@maljub01 maljub01 Mar 7, 2023

Choose a reason for hiding this comment

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

With #612 applied to my local setup, I am currently using this configuration to enable touch ID:

  # Enable sudo authentication with Touch ID
  system.patches = [
    (pkgs.writeText "pam.patch" ''
      --- a/etc/pam.d/sudo
      +++ b/etc/pam.d/sudo
      @@ -1,4 +1,6 @@
       # sudo: auth account password session
      +auth       optional       ${pkgs.pam-reattach}/lib/pam/pam_reattach.so  # Needed for using Touch ID within tmux
      +auth       sufficient     pam_tid.so
       auth       sufficient     pam_smartcard.so
       auth       required       pam_opendirectory.so
       account    required       pam_permit.so
    '')
  ];

This works pretty well and gets applied on every update. It is quite resilient and will continue to work even if I make changes to the patch (ex. changing the tmux related comment). This is because system.patches will reverse outdated patches before applying new ones.

Copy link
Contributor

@lilyball lilyball left a comment

Choose a reason for hiding this comment

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

Given the potential issues with the scenario where the user edits /etc/pam.d/sudo by hand, I think system.patches is a better approach. As I already commented, the activation script for system.patches appears as though it should reapply the patch on every activation, which makes me wonder why this module claims it won't.

Alternatively you could go with environment.etc and just add your own activation script that runs before activationScripts.etc and, if the option is disabled, checks if /etc/pam.d/sudo is a symlink to /etc/static/pam.d/sudo and replaces it with /etc/pam.d/sudo.orig instead (throwing an error if the latter doesn't exist).

I can personally confirm that using a symlink for /etc/pam.d/sudo works perfectly fine, I've been doing this myself for a while.

Comment on lines +27 to +29
${sed} -i '2i\
auth sufficient pam_tid.so # nix-darwin: ${option}
' ${file}
Copy link
Contributor

Choose a reason for hiding this comment

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

This sed here assumes that nobody else has edited this file. If e.g. I delete the first line comment, then it will insert the pam_tid.so after the existing pam_smartcard.so. Or if I delete the comment and pam_smartcard.so then it will insert it after the auto required pam_opendirectory.so, which will make it not work at all.

'' else ''
# Disable sudo Touch ID authentication, if added by nix-darwin
if grep '${option}' ${file} > /dev/null; then
${sed} -i '/${option}/d' ${file}
Copy link
Contributor

Choose a reason for hiding this comment

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

Similarly this line here assumes that pam_tid.so only exists in the auth sufficient pam_tid.so. I don't know if there's any reason for it to exist otherwise, but I might add e.g. a comment about it that would also be deleted.

in ''
${if isEnabled then ''
# Enable sudo Touch ID authentication, if not already enabled
if ! grep 'pam_tid.so' ${file} > /dev/null; then
Copy link
Contributor

Choose a reason for hiding this comment

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

This grep can also be tricked. For example, if I add a comment containing the string pam_tid.so that will cause nix-darwin to skip actually enabling it.

Of course, with the previous system.patches approach any such changes would make the feature not work at all, but at least it wouldn't make potentially surprising changes to the file.

Comment on lines +49 to +51
(Note that macOS resets this file when doing a system update. As such, sudo
authentication with Touch ID won't work after a system update until the nix-darwin
configuration is reapplied.)
Copy link

Choose a reason for hiding this comment

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

Apologies for bumping this three year old PR, but I figured this is the best place to ask this.

Isn't this not actually true due to the system activation service being enabled by default, which will reapply the PAM edits upon boot?

Assuming I'm correct, I think that leaving this untrue (by default) clarification in the documentation will just confuse users.

Copy link
Contributor

Choose a reason for hiding this comment

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

What you linked doesn't run the entire activation script, it just runs part of it

${config.system.activationScripts.etcChecks.text}
${config.system.activationScripts.etc.text}
${config.system.activationScripts.keyboard.text}

I suppose this could have been updated to include the pam activation script too if someone thought about it. I actually didn't even realize this was here. Though at this point it's worth noting that #787 changes how all of this works.

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.