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

users: update properties for known users #1120

Merged
merged 10 commits into from
Oct 27, 2024
Merged

Conversation

Enzime
Copy link
Collaborator

@Enzime Enzime commented Oct 24, 2024

I'm leaving gid and isHidden for later PRs

  • Don't allow changing home directory of current user and root

@Enzime Enzime force-pushed the push-ltvtsllrqvqy branch from 346bf84 to 2b15b32 Compare October 25, 2024 05:18
@Enzime
Copy link
Collaborator Author

Enzime commented Oct 25, 2024

@emilazy this is ready to review :)

modules/system/shells.nix Outdated Show resolved Hide resolved
modules/users/default.nix Outdated Show resolved Hide resolved
Comment on lines 262 to 324
# Always update all properties on users to keep them inline with configuration
dscl . -create ${dsclUser} PrimaryGroupID ${toString v.gid}
${optionalString (v.description != null) "dscl . -create ${dsclUser} RealName ${lib.escapeShellArg v.description}"}
${optionalString (v.shell != null) "dscl . -create ${dsclUser} UserShell ${lib.escapeShellArg (shellPath v.shell)}"}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is dscl not deprecated for these tasks? I’m a little worried about having this long‐term split where we’re mixing sysadminctl and dscl when they might behave differently.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

https://ss64.com/mac/sysadminctl.html

sysadminctl only deals with creating, deleting users, changing passwords and secure token settings, as far as I can tell updating settings is still dscl's responsibility

''}
# This command doesn't do anything if the folder already exists which should be the case
# for $SUDO_USER and root, so it should be safe to run unconditionally.
${optionalString v.createHome "createhomedir -cu ${name}"}
Copy link
Collaborator

Choose a reason for hiding this comment

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

I assume there’s no automated migration of files or anything, right? Do we want to be automating home directory changes, at least without a warning of some kind for this? We could split the difference by erroring out when the directory is wrong and printing commands to update it, which is an approach I like for avoiding automating “destructive” migrations while still making sure the activated configuration remains accurate.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I've changed the PR to only run createhomedir on user creation (as sysadminctl -addUser -home won't create the home directory), I've removed the logic that updates home and added a check to ensure the home directory set in the config matches the system for all users.

modules/users/user.nix Outdated Show resolved Hide resolved
@Enzime Enzime force-pushed the push-ltvtsllrqvqy branch 2 times, most recently from 4a04398 to a9f2082 Compare October 26, 2024 03:45
@Enzime
Copy link
Collaborator Author

Enzime commented Oct 26, 2024

@emilazy this should be ready to review again :)

The other change I've made is to move all the checking logic to system.checks.text to run first and avoid partial activations

@Enzime Enzime force-pushed the push-ltvtsllrqvqy branch from a9f2082 to be98f2d Compare October 26, 2024 04:52
@Enzime
Copy link
Collaborator Author

Enzime commented Oct 26, 2024

Turns out system.checks runs in user activation (presumably because it needs to run first) not root activation...

So I've updated my checks code to handle running from user activation level

@Enzime Enzime changed the title users: update most properties for known users users: update properties for known users Oct 26, 2024
@Enzime Enzime force-pushed the push-ltvtsllrqvqy branch 2 times, most recently from 85f685b to 4b18b7b Compare October 26, 2024 05:25
@Enzime
Copy link
Collaborator Author

Enzime commented Oct 26, 2024

@emilazy should be good to review now :)

emilazy
emilazy previously approved these changes Oct 26, 2024
Copy link
Collaborator

@emilazy emilazy left a comment

Choose a reason for hiding this comment

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

Changes LGTM, thanks! Some comments but I’m happy for this to be merged as‐is.

@@ -86,6 +86,7 @@ in
exit $_status
'';

# FIXME: activationScripts.checks should be system level
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
# FIXME: activationScripts.checks should be system level
# FIXME: activationScripts should be system level

😆


users.gids = mkMerge gids;
users.uids = mkMerge uids;

# NOTE: We put this in `system.checks` as we want this to run first to avoid partial activations
# however currently that runs at user level activation as that runs before system level activation
# TODO: replace `$USER` with `$SUDO_USER` when system.checks runs from system level
Copy link
Collaborator

Choose a reason for hiding this comment

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

FWIW I plan to make sure $SUDO_USER doesn’t leak through to avoid activating‐user dependencies creeping back in when user activation is gone. But that’s okay since we can just use primaryUser or I guess check currently logged‐in users or something.

fi
if [[ "$fullDiskAccess" != true ]]; then
printf >&2 '\e[1;31merror: users cannot be %s without Full Disk Access, aborting activation\e[0m\n' "$2"
Copy link
Collaborator

Choose a reason for hiding this comment

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

Wasn’t there a separate setting to manage users and groups? I avoid granting Full Disk Access to my terminal so this feels a little unfortunate, but better to check than not.

BTW, if we work on moving things out of activation scripts to launchd daemons as NixOS is doing we might be able to avoid this.

(Edit: Sorry, I didn’t realize all this was already present when reviewing! I should have made these comments when it was added. None of this is a blocker, given that.)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

After further investigation, there's a TCC service called kTCCServiceSystemPolicySysAdminFiles which shows the message: %s would like to administer your computer. Administration can include modifying passwords, networking, and system settings.

I believe this service is a subset of Full Disk Access which is the reason why granting FDA will not prompt you for this TCC service.

It turns out this permission gets triggered when you try to change a user's home directory as that can allow bypassing TCC (as TCC has a per-user database in a user's home directory) see: https://wojciechregula.blog/post/change-home-directory-and-bypass-tcc-aka-cve-2020-27937/

The easiest way to trigger this prompt:

$ sudo dscl . -change /Users/_nixbld1 NFSHomeDirectory /var/empty /var/empty 

-change here is safe as it won't change the value unless the first argument matches the current value:

$ dscl . -read /Users/_nixbld1 NFSHomeDirectory
NFSHomeDirectory: /var/empty
$ sudo dscl . -change /Users/_nixbld1 NFSHomeDirectory /private/var/empty /tmp
<main> attribute status: eDSAttributeNotFound
<dscl_cmd> DS Error: -14134 (eDSAttributeNotFound)
$ dscl . -read /Users/_nixbld1 NFSHomeDirectory
NFSHomeDirectory: /var/empty

When running this command from a graphical session, you'll be able to see the prompt. However, if you run it from SSH, there will be no prompt and you'll get permission denied:

$ sudo dscl . -change /Users/_nixbld1 NFSHomeDirectory /var/empty 
<main> attribute status: eDSPermissionError
<dscl_cmd> DS Error: -14120 (eDSPermissionError)

On the Remote Login modal, there's a checkbox Allow full disk access for remote users which is equivalent to toggling FDA for sshd-keygen-wrapper. I chose to suggest enabling it in the FDA menu just because it's easier to open:

$ open "x-apple.systempreferences:com.apple.preferences.sharing?Services_RemoteLogin"
$ sleep 2
$ open "x-apple.systempreferences:com.apple.preferences.sharing?Services_RemoteLogin"

On newer versions of macOS, this deep link will work on the first try however on older versions it requires calling it twice with a sleep to make sure the Sharing page loaded before trying again.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

With this information, I think we can change the checking logic to request kTCCServiceSystemPolicySysAdminFiles and then if that fails, give the user a relevant message:

If they're running over SSH, tell them to enable FDA for SSH or that they'll need to not use SSH with darwin-rebuild.
If they're running locally, tell them to wait 1 minute before running darwin-rebuild switch again and make sure to accept the prompt and possibly mention turning on FDA for their terminal emulator.

modules/users/default.nix Outdated Show resolved Hide resolved
modules/users/default.nix Outdated Show resolved Hide resolved
name = lib.escapeShellArg v.name;
dsclUser = lib.escapeShellArg "/Users/${v.name}";
in ''
${optionalString cfg.forceRecreate ''
Copy link
Collaborator

Choose a reason for hiding this comment

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

We decided we could just drop this for now, right? Might be best to do now to avoid adding more logic we have to verify the correctness of when we’d like to kill it off.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'll remove it in my next PR 👍

Comment on lines +179 to +201
if [[ ${lib.escapeShellArg v.home} != "$homeDirectory" ]]; then
printf >&2 '\e[1;31merror: config contains the wrong home directory for %s, aborting activation\e[0m\n' ${name}
printf >&2 'nix-darwin does not support changing the home directory of existing users.
printf >&2 '\n'
printf >&2 'Please set:\n'
printf >&2 '\n'
printf >&2 ' users.users.%s.home = "%s";\n' ${name} "$homeDirectory"
printf >&2 '\n'
printf >&2 'or remove it from your configuration.\n'
exit 1
fi
Copy link
Collaborator

Choose a reason for hiding this comment

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

💜

We could print instructions on how to manually update it? Not necessary for an MVP though.

type = types.either types.shellPackage types.path;
default = "/sbin/nologin";
type = types.nullOr (types.either types.shellPackage types.path);
default = "/usr/bin/false";
Copy link
Collaborator

Choose a reason for hiding this comment

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

You can break your shell by starting to manage your user with nix-darwin unless you explicitly null this out, right? I was thinking we could just make null create users with /usr/bin/false but otherwise act the same way as the default. Not sure if that’s a good idea or not.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is a good catch, I think this bug has been around since #818 and I'm not sure if anyone has been hit by it yet as it's currently gated behind users.knownUsers and my suspicion is that most users only add themselves to users.knownUsers when they want to update their shell as most other options are less useful.

I'll add the change to make null create users with /usr/bin/false and but still mean don't update this user's shell. This change should be superseded by adding a type option because then we could not pass the -shell flag to sysadminctl and it would set the correct shell based on whether -roleAccount was passed or not (alternatively we could determine the default passed on the type in nix-darwin as well)

@Enzime
Copy link
Collaborator Author

Enzime commented Oct 27, 2024

@emilazy this is ready for review :)

Copy link
Collaborator

@emilazy emilazy left a comment

Choose a reason for hiding this comment

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

Looks great, thank you for seeing this through! Feel free to merge.

My one remaining nit is that sudo dscl . -change /Users/nobody NFSHomeDirectory "$homeDirectory" "$homeDirectory" scares me a little and it’d be nice if we could just touch some meaningless NixTestWhatever field somewhere, maybe even not attached to a user at all. But I think it should probably be fine and I’m just being paranoid.

@Enzime Enzime merged commit 5c0c6aa into LnL7:master Oct 27, 2024
5 of 6 checks passed
@Enzime Enzime deleted the push-ltvtsllrqvqy branch October 27, 2024 23:08
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.

2 participants