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

fix: move pam configuration to sudo_local #1344

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 62 additions & 50 deletions modules/security/pam.nix
Original file line number Diff line number Diff line change
@@ -1,69 +1,81 @@
{ config, lib, pkgs, ... }:

with lib;

let
cfg = config.security.pam;

# Implementation Notes
#
# We don't use `environment.etc` because this would require that the user manually delete
# `/etc/pam.d/sudo` which seems unwise given that applying the nix-darwin configuration requires
# 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).
#
# As such, we resort to line addition/deletion in place using `sed`. We add a comment to the
# added line that includes the name of the option, to make it easier to identify the line that
# should be deleted when the option is disabled.
mkSudoTouchIdAuthScript = isEnabled:
let
file = "/etc/pam.d/sudo";
option = "security.pam.enableSudoTouchIdAuth";
sed = "${pkgs.gnused}/bin/sed";
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
''}
'';
in

{
options = {
security.pam.enableSudoTouchIdAuth = mkEnableOption "" // {
description = ''
Enable sudo authentication with Touch ID.
security.pam = {
enable = lib.mkEnableOption "managing PAM with nix-darwin" // {
Comment on lines +8 to +9
Copy link
Collaborator

Choose a reason for hiding this comment

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

I take it this is to accommodate users that want a custom sudo_local file?

I’m wondering if this should be security.pam.services.sudo_local.enable. That’s not an option that exists in NixOS, but it brings us closer to NixOS’s structure for security.pam.*. We’re just managing the sudo_local configuration rather than PAM as a whole here.

I might prefer something like @mirkolenz’s solution, which could also increase NixOS compatibility: we could have security.pam.services.sudo_local.text which is lib.types.lines and populate it with our settings by default; users who want extra configuration could use lib.mkAfter; users who want to replace the entire configuration could override it completely. If someone really wants a totally unmanaged sudo_local file, we could make environment.etc."pam.d/sudo_local".enable just a lib.mkDefault so that they can disable it directly. (Or we could keep the non‐standard .enable, but I think in the presence of .text it would be niche enough that it’s fine to expect someone to toggle the file like that.)

(if not for .text, we could make it security.pam.services.sudo.* since we’re effectively extending the sudo configuration. But if we add .text – which seems nicer than .enable here – then we should make sure it’s sudo_local, as security.pam.services.sudo_local.text will not be able to overwrite the entire PAM sudo service configuration, as a NixOS user or configuration might expect.)

default = true;
example = false;
};

enableSudoTouchIdAuth = lib.mkEnableOption "" // {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Per the previous comment, since we’re adding more options and moving stuff around, this could be renamed to security.pam.services.sudo_local.touchIdAuth to match the NixOS structure.

description = ''
Whether to enable Touch ID with sudo.
When enabled, this option adds the following line to
{file}`/etc/pam.d/sudo`:
This will also allow your Apple Watch to be used for sudo. If this doesn't work,
you can go into `System Settings > Touch ID & Password` and toggle the switch for
your Apple Watch.
'';
};

```
auth sufficient pam_tid.so
```
enableSudoPamReattach = lib.mkEnableOption "" // {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Similarly, this could be introduced as security.pam.services.sudo_local.reattach.

description = ''
Whether to enable reattaching a program to the user's bootstrap session.
::: {.note}
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.
:::
'';
This fixes Touch ID for sudo not working inside tmux and screen.
This allows programs like tmux and screen that run in the background to
survive across user sessions to work with PAM services that are tied to the
bootstrap session.
'';
default = cfg.enableSudoTouchIdAuth;
Copy link
Collaborator

Choose a reason for hiding this comment

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

As I said in the previous PR I think we shouldn’t turn this on by default. It brings a third‐party PAM module into the sudo chain which I don’t think we should be doing implicitly, and I think most users probably don’t need it. IIRC it relies on undocumented private APIs too.

example = false;
};
};
};

config = {
system.activationScripts.pam.text = ''
environment.etc."pam.d/sudo_local" = {
inherit (cfg) enable;
text = lib.concatLines (
(lib.optional cfg.enableSudoPamReattach "auth optional ${pkgs.pam-reattach}/lib/pam/pam_reattach.so")
++ (lib.optional cfg.enableSudoTouchIdAuth "auth sufficient pam_tid.so")
Comment on lines +44 to +45
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: I’d prefer to trim the unnecessary alignment whitespace here, but it doesn’t matter.

);
};

system.activationScripts.pam.text =
let
file = "/etc/pam.d/sudo";
marker = "security.pam.sudo_local";
deprecatedOption = "security.pam.enableSudoTouchIdAuth";
sed = lib.getExe pkgs.gnused;
in
''
# PAM settings
echo >&2 "setting up pam..."
${mkSudoTouchIdAuthScript cfg.enableSudoTouchIdAuth}
# REMOVEME when macOS 13 no longer supported as macOS automatically
# nukes this file on system upgrade
# Always clear out older implementation if it is present
if grep '${deprecatedOption}' ${file} > /dev/null; then
${sed} -i '/${deprecatedOption}/d' ${file}
fi
${if cfg.enable then ''
# REMOVEME when macOS 13 no longer supported
# `sudo_local` is automatically included after macOS 14
if ! grep 'sudo_local' ${file} > /dev/null; then
${sed} -i '2iauth include sudo_local # nix-darwin: ${marker}' ${file}
fi
'' else ''
# Remove include line if we added it
if grep '${marker}' ${file} > /dev/null; then
${sed} -i '/${marker}/d' ${file}
fi
''}
'';
};
}