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: per user activation #1016

Closed
wants to merge 2 commits into from
Closed

Conversation

dlubawy
Copy link

@dlubawy dlubawy commented Jul 25, 2024

Would partially solve some of the issues around per user activation scripts (ex: #554). Summary of the issue is that system.defaults does not get applied to other users on activation. This is because each call to defaults write must be done by each user for the preferences to actually apply (i.e. root/admin cannot call this on their behalf). The only solution is to run each of the defaults write calls with sudo -u for each configurable user (those within /Users) to ensure system.defaults are actually applied on the whole system for all users. Note: this means the user running darwin-rebuild must also be in sudoers and capable of using sudo -u for each user.

I think the real solution for per user scripts is the one discussed elsewhere (ex: #96) and remove them entirely in favor of home-manager. Until that happens though, this is the only method I know to fix activation in a multi-user system for configs that should be applied to all users.

@emilazy
Copy link
Collaborator

emilazy commented Aug 13, 2024

Following on from #1017 (comment):

The original sin, as I see it, is the dependence of activation on the identity of the activating user. Everything will get better (consistency with NixOS, coherent model as a system manager, boot‐time activation, multi‐user systems, …) if we eliminate that. The whole concept of activate-user needs to go. Everything that relates to a specific user needs to be scoped under users.users.*, or have a user option if it’s a singleton (our Homebrew module works like this currently, although maybe it should be per‐user instead), or be migrated over to Home Manager. Activation should run entirely as root and use sudo when it wants to poke at a specific user.

Although I think we’re in complete agreement about the desirable end state, I’m not minded to go with the approach you have here, because it’s a breaking change that doesn’t directly address the root of the problem. Of course, the problem is that addressing the root of the problem is hard. Just ripping out user activation is easy; I have a branch from 2023 lying around that simply migrates uses of launchd.user.agents to launchd.agents, rips out all user defaults support, adds compatibility stubs for rollbacks, and works great.

The problem, of course, is the part where we rip out functionality that probably the majority of existing nix-darwin configurations use! We need a migration plan. Thankfully, I have one. Unfortunately, it’s a little fussy, the first step can’t be done by an external contributor, and I’ve not been great at doing the time management to get around to putting it into practice. All of this should be written up in more detail and put in an issue rather than being left as a PR comment, but I’ve left this long enough so I want to fill you in on my thinking even if it’s rough. I’m grateful for your offer to help out with the roadmap and hopefully we can actually get started on this soon!

For the longest time I was blocked on the fact that there are a lot of moving parts here, the desired end result will inevitably involve breaking existing user configurations, and there are other things we should really fix along the way so that users don’t have to do multiple mass migrations of their configuration. However, talking on Matrix (#nix-darwin-dev:matrix.org; we should really advertise this and probably get it in the NixOS space) I realized that there’s a minimum possible effort stop‐gap that lets us do a lot of the migration with very minimal breakage and then work on the long tail with less urgency. Here’s the idea:

  1. Actually cut some release branches, separate out the manual to be per‐branch and move all our other documentation into it so people consult the right material, and add Nixpkgs version checks to ensure that people are on the right ones. We’ve needed these for a long while (cf. RFC: Start using stable branches tied to Nixpkgs releases #727), and I’d feel a lot more comfortable doing anything that requires configuration migration and changing how activation works if I know that only people on the bleeding edge will be immediately faced with it.

  2. Add a darwin.primaryUser option and stub out activate-user. The idea is simple: everything that currently depends on the activating user through user‐specific activation will apply to the darwin.primaryUser you set through sudo. No multiple user support for those options, no moving them around, just a complete brute force solution to kill off user activation. If you – correctly – think this is weird and gross and that the vast majority of the options it applies to shouldn’t be managed by nix-darwin anyway, you can simply avoid setting it and evaluation will error out if you ever use a forbidden option. Basically, this means that we can indicate all the user activation options as legacy off the bat and warn people that the model will change in the future, while ensuring that users only have to add one trivial line to their configuration to keep it working.

At that point, we will have a system with a nicer architecture but an interface that is still confused and somewhat more obviously silly than before. At that point, the task becomes getting rid of darwin.primaryUser, so we need to decide what to do about all those options.

There may be some of them that it makes particular sense for us to keep under users.users.*, but I think that the best solution is to deduplicate and migrate as much as possible to Home Manager. There are plenty of Nix‐on‐macOS users who use Home Manager but not nix-darwin, and it doesn’t really make any sense to deprive them of functionality they could otherwise have, or stretch our limited maintainer efforts by maintaining functionality in both places. We will of course have to coordinate with the Home Manager maintainers for this, and we might want to consider bringing Home Manager integration in‐tree so that people can more seamlessly access the migrated options.

For modules that just set up a launchd user agent or similar, we could just migrate launchd.user.agents to launchd.agents and be done with it, but for the reasons I gave it really probably makes more sense to move that stuff into Home Manager for the most part. it should be pretty easy to do this migration and deduplication, and there aren’t so many of these modules that it should be a huge burden on users to deal with it manually (though I’d prefer to avoid that if possible). The big elephant in the room is user‐specific defaults support. People love it! And of course they should; a huge amount of what it means to configure a macOS system declaratively consists of twiddling user‐specific defaults. Home Manager has its own implementation of this, but that implementation is not fully compatible with nix-darwin, both in terms of interface and implementation. Getting that part of the migration right is what’s really tricky and why I stalled on making all this happen until I thought of the darwin.primaryUser stopgap. (Note that system‐wide defaults do not depend on the activation user, don’t make sense to move to Home Manager, and must stay in nix-darwin.)

For that, here’s my thinking:

  1. We kind of really want migration smart enough that it can automatically spit out Home Manager Nix code that sets all the defaults that are in the user’s existing configuration. That’s doable, for sure, but it’s certainly effort.

  2. We really ought to deduplicate the implementations for things like launchd agent handling and code to set defaults with Home Manager to ensure compatibility and reduce maintenance effort across the ecosystem. Last time I looked into it, both the nix-darwin and Home Manager defaults implementations seemed broken in different ways. And a big huge flaw that frustrates me every time I think of it is that our current implementations are not declarative; removing an option leaves it set to whatever you had before removing it! We’re basically no better than Ansible here.

    That’s fixable, and I think it’s important to fix, but it will of course be another big change that may require user intervention. I think we ought to coordinate it with the rest of this migration, so that we’re not switching up how defaults work on people twice, and so that we don’t migrate stuff to an implementation that doesn’t necessarily match how ours works. We’ll also need to coordinate it with Home Manager, of course. My plan was to get approval in principle for the idea of having shared code for these things that achieves a higher level of declarativity, work on it out‐of‐tree, and then figure out how we’re going to use it in both nix-darwin and Home Manager.

    This comment is already too long, but there are two possible approaches here:

    1. Just diff the defaults to be set between the previous and current generation, and defaults delete anything that’s no longer present. This is nice and simple and e.g. matches how launchd daemon handling works in nix-darwin. It has some issues, though, like around boot‐time activation or the possibility of ever supporting darwin-rebuild boot. It also means that if a user tweaks a setting, then changes it in Nix, then deletes that Nix change, they lose their original tweak. That’s probably not the end of the world though.

    2. Explicitly track state. Keep a mutable record of what we’ve set, and the values things should go back to when we find we’re no longer setting something in that list. (You might, say, have to do a one‐time opt‐in to “just delete stuff, give me the system defaults if I change it” or “actually, I really like my exact current setup, save all those values and restore to them if I ever stop setting something in Nix”, which is how we’d handle migration. Though I don’t know how that would handle new options, it really needs more thinking…)

As you can see, getting to an actually good end‐state here without making people rewrite their entire configurations is… kind of tricky. But just getting rid of user activation and allowing forward‐thinking people to avoid all the stuff we need to change is quite practical, and I will try to get around to doing that soon. Discussing and collaborating on the rest of the owl is definitely something I’m interested in and I’d appreciate your thoughts on the plan in general!

@dlubawy
Copy link
Author

dlubawy commented Aug 25, 2024

@emilazy thank you for the detailed comment with your thoughts on this matter. I think a lot of this makes sense. You are certainly right. The first step, regardless of the design choices, should be in getting release branches done. Any changes to user activation should be built on a stable foundation where users can pin their systems to some level of expectation.

The idea to create a temporary darwin.primaryUser for a phased transition makes sense. That would quickly move most things away from per user activation like you said. It would also make it immediately clear that those settings will only ever work for one user rather than apply system-wide (which the docs should be changed to reflect; i.e. call them primaryUserDefaults instead). That was my problem coming in as a new user here. I was under the impression that system.defaults settings would apply globally to the whole system like what. It then became a shock to me when I used nix-darwin to create a new user and then that user had none of those settings applied to it because the activation scripts only run for the user executing darwin-rebuild and the defaults write command only ran once for that user only.

I think everything else you wrote looks solid and well thought out too. As a still fresh user, I certainly don't have much more to add in this topic and can only thank you for indulging me in my curiosity and desire to build something better. My only hope is that one day mainline nix-darwin will have closer parity to NixOS whereby multi-user configurations are more achievable without resorting to custom forks. I think your plan could get us closer, and from its results I think we could use it as a base to introduce other changes to items like user management that I mention in #1017.

I'll go ahead and close this PR since it seems irrelevant given the plan you provided and the incompatibility this PR has to that approach.

@dlubawy dlubawy closed this Aug 25, 2024
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.

3 participants