Munki 7, App Management TCC, and supervisor

270 views
Skip to first unread message

Gregory Neagle

unread,
Sep 9, 2025, 11:52:44 AM (5 days ago) Sep 9
to munki-dev
In Munki 6.3, to deal with the new App Management privacy control introduced in macOS Ventura, a new compiled tool was developed. Internally, it’s called “munkishim”, and serves to launch the managedsoftwareupdate process in a way that admins can use MDM to grant App Management permissions.

This video covers the problem, and the solution adopted in Munki 6.3 (and continuing through Munki 6.7):

One reason this issue was tricky to address in Munki 6 is that most of the command-line tools in Munki 6 are actually Python _scripts_, and it’s not possible to usefully sign Python scripts in a way that’s helpful to grant PPPC permissions via MDM.

In Munki 7, the command-line tools are all compiled Swift executables and can be signed. This eliminates the need for the munkishim.

But managedsoftwareupdate is still often launched by /usr/bin/munki/supervisor. Due to how macOS determines the “responsible process” this means that sometimes “supervisor” will be the responsible process. But if an admins runs managedsoftwareupdate via the Terminal, _managedsoftwareupdate_ will be the responsible process…

This is problematic for a couple of reasons:
1) Prompts to allow “supervisor” to access various PPPC permissions might be confusing
2) Admins will need to use MDM to give both “supervisor” and “managedsoftwareupdate” PPPC permissions.

An alternative might be to adopt a tactic used by the munkishim tool, and modify supervisor to disclam responsibility for its children. IOW, when supervisor launches managedsoftwareupdate, it tells the OS it’s not responsible for managesoftwareupdate. This then causes the OS to treat managedsoftwareupdate as the responsible process.

The advantage to this approach would be that _only_ managedsoftwareupdate might trigger PPPC/TCC prompts, and admins need only use MDM to approve managesoftwareupdate to have App Management or Full Disk Access rights (or any other TCC rights you wanted to grant it).

Curious about others’ thoughts here.

-Greg

Alan

unread,
Sep 9, 2025, 12:35:19 PM (5 days ago) Sep 9
to munk...@googlegroups.com
Thanks for laying this out, Greg.

Since supervisor is a compiled binary, I'm assuming it can't be hijacked to invoke something apart from managedsoftwareupdate? Maybe that assumption is wrong. If it can't be hijacked, I'm all for just having it be signed (which it appears to be in the latest beta builds from macadmins/munki-builds). If an admin had to make a PPPC MDM profile for managedsoftwareupdate, it isn't that much extra work to add supervisor to that existing profile.

Maybe I'm missing a key downside to keeping it as is.

--
Find related discussion groups here:
https://github.com/munki/munki/wiki/Discussion-Group
---
You received this message because you are subscribed to the Google Groups "munki-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to munki-dev+...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/munki-dev/ED0D5F89-2CAA-4C17-A0B8-259FE95D2833%40mac.com.

Gregory Neagle

unread,
Sep 9, 2025, 3:25:04 PM (5 days ago) Sep 9
to munki-dev
Actually you point out another risk of “keeping it as it is”:

Currently supervisor can launch any other binary at all: it’s not hard-coded to launch managedsoftwareupdate.

If an admin gave supervisor Full Disk Access, anyone could then use supervisor to launch any other binary with Full Disk Access.

So it feels like we must do one of the followiing:

1) Modify supervisor so that it _only_ can launch managedsoftwareupdate.
2) Modify supervisor so it disclaims responsibility for its children (preventing any PPPC/TCC permissions from being passed to its children)
3) Eliminate supervisor altogether.

-Greg

Kevin M. Cox

unread,
Sep 9, 2025, 4:05:08 PM (5 days ago) Sep 9
to munki-dev
I think it comes down to whatever is the easiest to support from a development standpoint.

As Alan did, I was originally going to say "it isn't hard to just add PPPC for supervisor" for a MacAdmin.

All three of those new options work for me administratively, so it comes down to which is easiest to code/maintain.

~ Kevin

Rob Renstrom

unread,
Sep 9, 2025, 6:23:47 PM (5 days ago) Sep 9
to munki-dev
To expand on the idea of eliminating supervisor altogether, I wonder if managedsoftwareupdate could incorporate and handle the --delayrandom and --timeout flags from supervisor, to create a "supervisor mode" that performs these functions, monitoring the timeout to terminate the process and handling the random delay. Perhaps it could recursively call itself without these flags for the actual run.

Gregory Neagle

unread,
Sep 9, 2025, 7:36:32 PM (5 days ago) Sep 9
to munki-dev
i am considering that as a possibility.

Some challenges:

- Right now managedsoftwareupdate, when launched, looks to see if there are any other instances of managedsoftwareupdate running, and if so, exits. We’d need to figure out how distingusih between “that other instance of managedsoftwareupdate is actively running” and “that other instance of managedsoftwareupdate is randomly sleeping before continuing”. Without that, a high percentage of manual runs of managedsoftwareupdate (either directly, or via Managed Software Center.app) will fail very early because there is another instance sleeping.

- Adding this functionality to managedsoftwareupdate increases its complexity. Can it then be trusted to monitor itself and force itself to quit when it has run “too long”? I guess it could launch a second monitor process that exists only to kill its parent if it lives too long…

Another approach might be to refactor things such that supervisor never _directly_ launches managedsoftwareupdate; instead it triggers a launchdaemon, and launchd then directly runs managedsoftwareupdate. I think this would avoid supervisor ever being identified as the responsible process (and also would avoid using the undocumented/unsupported/private method to launch a process while disclaiming responsibility for it.

-Greg

koryl...@gmail.com

unread,
Sep 10, 2025, 2:22:14 AM (5 days ago) Sep 10
to munki-dev
Looking at the options mentioned already (which I think covers all of the reasonable options):

Option 1: Make a swift supervisor, have it only allow calling managedsoftwareupdate, and have it disclaim the process
Pros:
  • Simplest (?) to implement, similar to what exists today
Cons
  • Keeps supervisor
  • Relies on private API to disclaim process

Option 2: Make a swift supervisor that triggers managedsoftwareupdate via launchd
Pros:
  • Seems simple to implement (IIRC, munki has or had code to do something similar to this already)
  • doesn't need to use any private APIs
Cons:
  • Keeps supervisor
"Using more launchd" could go in Pros or Cons depending on how much you like launchd :)


Option 3: Integrate supervisor into managedsoftwareupdate itself
Pros:
  • drops supervisor
  • doesn't need to use any private APIs
Cons
  • introduces more complexity into managedsoftwareupdate

Option 2 seems like the best balance, as long as dealing with launchd doesn't make thinks too messy.

I do think Option 3 is the cleanest, if you're willing to deal with more complexity.

I'm no swift expert, but doing some comparisons with languages I'm familiar with, here's how you might solve the issues with integrating the supervisor into managedsoftwareupdate:

To deal with "only one managedsoftwareupdate running at a time" issue, you can use flock(2) to share an exclusive lock between multiple processes. e.g. open a lock file (/var/run/managedsoftwareupdate.lock) with exclusiveLock and nonBlocking options. So open the lock when starting managedsoftwareupdate, and release it when managedsoftwareupdate is done.

To handle the "watchdog" aspect of stopping itself when its run too long, you could run another instance of managedsoftwareupdate in another process, or you could run it in another thread, and just kill the subprocess or thread if it runs too long.

gregn...@mac.com

unread,
Sep 10, 2025, 12:07:14 PM (4 days ago) Sep 10
to munki-dev
supervisor has two purposes: 

  1) randomize Munki's periodic run so that every client doesn't hit the server at the same time; 
  2) kill a managedsoftwareupdate process that's been running "too long". 

For #1:
  - The launchd job that does the periodic background runs could switch from a calendar interval (every hour at XX)  to a time interval (every 60 minutes) and then rely on the natural splay of boot times to distribute the load. Risk: sites that automate or schedule reboots might have a large number of Macs with similar/identical boot times.
  - Or the installer could modify the launchd plist for the periodic background job, writing a random minute for StartCalendarInterval. Risk: this approach might conflict with anything that audits/manages expected state of things like launchd plists. (which might include things like Puppet/Chef/Salt)

For #2:
  - When a periodic (or even manual) run of managedsoftwareupdate sees an existing managedsoftwareupdate process, it could see how long it's been running, and if "too long", kill it. No need for a monitoring process at all.

Nick McSpadden

unread,
Sep 10, 2025, 1:09:15 PM (4 days ago) Sep 10
to munk...@googlegroups.com
I would be more in favor of the "supervisor uses launchd to trigger managedsoftwareupdate and should be limited to only doing that and nothing else." I agree that from an overall management standpoint, I don't like a binary that is an unbounded "free full disk access to anything I pass to it". It also reduces the complexity of supervisor a bit to primarily just needing to validate if the existing Munki run is going too long/already running. The downside, of course, is that there's still always uncertainty about the future of launchd and at some point Apple will likely force this to change to something else, but that will already be true for all the other launchds Munki depends on.

In terms of the two purposes:
#1 supervisor could also insert its own random splay by sleeping - this is what Chef does. That way the launchd is static. I would really not want to repeatedly modify the launchd plist, or have non-deterministic launchd configurations, as that would be enormously challenging for any org that tries to manage that launchd externally (or validate it hasn't been tampered, etc.). I do want to keep the "don't everybody hit the server at the same exact second" functionality although my company doesn't have any setups for which this would realistically be a factor.

If supervisor were to disappear, managedsoftwareupdate itself could incorporate its own random sleep splay feature (and if so desired, could be configurable, but that's unlikely to be a priority or even necessary given that it's behaved this way already forever).

#2 - agreed that managedsoftwareupdate itself could also incorporate this, or as others have suggested, use some kind of lockfile pattern (with all the tradeoffs and pros/cons that would entail).

Generally speaking, for a management tool, I would want to see:
- All the contents of the Munki payload are identifiable, verifiable, and we can tell if they've been tampered with in some way. The launchds in particular are things we manage here at Meta directly via Chef, to ensure that people can't disable or modify them. A dynamic launchd would be quite a problem for this model, and selfishly I don't want that (nor do I think it's a great model).
- A single signed binary to give relevant permission to (PPPC and such) that will handle everything it needs to and can be properly attributed and logged. We can give that binary the permissions it needs, we can tell when it's running with our various monitoring tools, and we can rest assured that it isn't doing anything else (like being used to bootstrap some other non-Munki process with full disk access, etc.).

Whether supervisor needs to be a separate binary for that is more of an implementation detail and I could see it going either way, but the intended functionality it provides is valuable and necessary.



--
--
Nick McSpadden
nmcsp...@gmail.com

gregn...@mac.com

unread,
Sep 10, 2025, 5:06:11 PM (4 days ago) Sep 10
to munki-dev
Here is the direction I am leading toward:
  • Change the LaunchDaemon at com.googlecode.munki.managedsoftwareupdate-check:
    • Remove StartCalendarInterval
    • Add StartInterval with a value of 3600 (seconds).
    • As a result, the first background run of managedsoftwareupdate will happen one hour after boot (or install/reinstall/upgrade of the munkitools), and every hour afterwards.
  • Modify the check `managedsoftwareupdate` does at launch when checking if another instance of managedsoftwareupdate is running: if another instance is running, examine how long it has been running. If longer than 43200 seconds (12 hours), kill that instance and continue with the current instance.
  • Eliminate `supervisor` altogether.
This avoids using a private API to disclaim responsibility for PPPC/TCC, eliminates one binary, and simplifies several launchd plists.

gregn...@mac.com

unread,
Sep 10, 2025, 7:10:18 PM (4 days ago) Sep 10
to munki-dev

Rod Christiansen

unread,
Sep 11, 2025, 12:01:07 AM (4 days ago) Sep 11
to munki-dev
+1 on moving the randomization to be set by to the one hour after boot/install/reinstall/upgrade

Nick McSpadden

unread,
Sep 11, 2025, 1:51:55 AM (4 days ago) Sep 11
to munk...@googlegroups.com
12 hours for the process seems quite long - I would recommend half that most. I think 4 hours is more than enough, so that if that triggers during a work day, there's a reasonable chance Munki can still succeed while a device is awake and active.

--
Nick McSpadden
nmcsp...@gmail.com


Gregory Neagle

unread,
Sep 11, 2025, 9:08:14 AM (3 days ago) Sep 11
to munk...@googlegroups.com, munk...@googlegroups.com
12 hours is what the timeout is now and has been for over ten years. 

Sent from my iPhone

On Sep 10, 2025, at 10:52 PM, Nick McSpadden <nmcsp...@gmail.com> wrote:



gregn...@mac.com

unread,
Sep 11, 2025, 11:18:39 AM (3 days ago) Sep 11
to munki-dev

Adam Anklewicz

unread,
Sep 11, 2025, 12:20:12 PM (3 days ago) Sep 11
to munk...@googlegroups.com
This all sounds good to me. I really appreciate how clear you communicate changes to the community, Greg.


--
Adam Anklewicz
Pronouns: he/him/his


gregn...@mac.com

unread,
Sep 11, 2025, 2:09:49 PM (3 days ago) Sep 11
to munki-dev
To me the last remaining concern might be sites that automate or schedule reboots/startup of a large number of Macs (perhaps in instructional labs), so that you'd have a large number of Macs with (nearly) identical startup times, leading to all of them hitting the Munki server at the same time.

It's not clear to me how big a concern this actually is. I'd think a lab of, say, 30 Macs that all started up at the same time would not put unmanageable load on a reasonably-provisioned Munki server. 1000 Macs might well cause issues. 

Also, in testing over the last day, I've seen that sleeping and waking shifts the "schedule":

If my Mac starts up at 07:45, the one hour interval means that managedsoftwareupdate runs in the background at approximately 08:45, 09:45, 10:45, etc. But if my Mac then goes to sleep such that one of those times is missed, the next run (after waking) does not (necessarily) occur at :45. It happens at some other time (determined by launchd) and then hourly after that, until the next sleep/wake event. So over time, even machines that start up at the same time will have their managedsoftwareupdate "schedule" drift apart from each other if the machines can sleep. (I imagine there _might_ be labs that prevent machine sleep...)

-Greg

gregn...@mac.com

unread,
Sep 11, 2025, 7:45:44 PM (3 days ago) Sep 11
to munki-dev
If an admin was concerned about a large group of machines all running managedsoftwareupdate on the same schedule, one approach to deal with that might be a script that ran at boot and did something like:

launchctl bootout system/com.googlecode.munki.managedsoftwareupdate-check
sleep $RANDOM_TIME_BETWEEN_0_AND_60_MINS
launchctl bootstrap system/ /Library/LaunchDaemons/com.googlecode.munki.managedsoftwareupdate-check.plist


or possibly

launchctl disable system/com.googlecode.munki.managedsoftwareupdate-check
sleep $RANDOM_TIME_BETWEEN_0_AND_60_MINS
launchctl enable system/com.googlecode.munki.managedsoftwareupdate-check


gregn...@mac.com

unread,
Sep 11, 2025, 9:44:14 PM (3 days ago) Sep 11
to munki-dev
While I'm making big changes (like removing supervisor), figured I'd rip the band-aid off on some other changes I'd been considering:

Moving all the helper tools out of /usr/local/munki and into /usr/local/munki/libexec:

Moving the (macOS 10.15/11) Swift runtime library from /usr/local/lib to /usr/local/munki/lib:

-Greg

MiqViq

unread,
Sep 12, 2025, 2:33:34 AM (3 days ago) Sep 12
to munki-dev
Hi Greg, all proposed changes seem to be fine with my environment.

Just to add another ingredient in the soup, I was thinking maybe to fix the issue with MSC status app taking over the loginwindow view.
https://github.com/munki/munki/issues/1225
No need to continue on this further here, but please consider and at least comment this in github.
I am willing to try to give my input for the code/testing on that matter, but I currently have limited time available for it as semester has started in our university...

-MiqViq

gregn...@mac.com

unread,
Sep 12, 2025, 11:37:33 AM (2 days ago) Sep 12
to munki-dev
A signed installer pkg with a test build including all of changes discussed here (removal of supervisor; change in the run scheduling of the background check, moving the helper tools out of /usr/local/munki) is available here: https://github.com/macadmins/munki-builds/releases/tag/v7.0.0.5309

-Greg
Reply all
Reply to author
Forward
0 new messages