Categories
Mac admin'ing

Allowing Outset-run scripts to have access to user folders

Because of TCC/PPPC, which Apple introduced in macOS 10.14, scripts and applications have to ask for permissions to do certain things, especially things like reading user home directory files.

If you have an Outset login script that tries to access something in the home directory, you may find in the ~/Library/Logs/outset.log that you get a Failure processing [name of script, command that failed] Operation not permitted error.

I tried creating a PPPC profile for the script itself. That didn’t work. I tried creating a PPPC profile for /usr/local/outset/outset. That didn’t work. I tried creating a PPPC profile for /bin/zsh. That didn’t work. I tried creating a PPPC profile that allowed all three to have access to all files. That didn’t work.

So, finally, I ran a tccutil reset All to reset the database, and then I logged in again, and it asked for Python to have access to the home folder the script was trying to read.

So I created a PPPC profile to allow Python (the one Outset is using) to have access to the home folder the script was trying to read, and the script ran just fine.

I’m not an expert on this, and any follow-up questions you have would probably be best directed to the #outset channel on the MacAdmins Slack (I’m over there too) instead of in the comments of this post (blog comments aren’t a great venue for tech support), but I thought sharing one case that worked might be helpful for others running into the same issue.

Categories
Mac admin'ing

If Jamf recon is launching a du process that causes a CPU spike

If Jamf inventory (jamf recon) causes an extended CPU spike specifically related to the du command, you can fix that by going, in the Jamf settings, to Computer Management > Computer Management – Management Framework > Inventory Collection, and then uncheck the Include home directory sizes checkbox.

That is a system-wide setting, but especially if most or all of your fleet is one-to-one Macs (not shared Macs), it’s a good idea to do. Your jamf recon inventory runs will still report back free disk space, just not broken down by individual home directories.

Special shoutout to tlark and neilmartin83 on the MacAdmins Slack for giving me more context around this.

Categories
Mac admin'ing

Terminal command to tell if a macOS directory is SIP-protected

Starting with El Capitan (OS X 10.11), Apple started using System Integrity Protection (SIP) in macOS, so that certain directories would be not writable, even by root. Here’s a quick reference for a couple of commands you can use to see if a directory or file is SIP-protected, as that may change from macOS version to macOS version.

Method 1

ls -lO (that’s a lowercase L, followed by a capital o, not the number 0), and look for restricted.

Example: ls -lO /Library/Updates/
total 2224
-rw-r--r--@ 1 root wheel restricted 181 Jul 29 10:22 PPDVersions.plist
-rw-r--r--@ 1 root wheel restricted 1130219 Jul 29 10:22 ProductMetadata.plist
-rw-r--r-- 1 root wheel restricted 260 Jul 29 10:17 index.plist

Method 2

xattr -l (that’s a lowercase L) and then the name of the directory. Look for com.apple.rootless

Example: xattr -l /Library/Updates/
com.apple.rootless: SoftwareUpdate

Special thanks to @revolize and @Magneto on the MacAdmins Slack!

Categories
Mac admin'ing

Running daily, weekly, and monthly scripts in macOS using periodic

Background

I was looking for time-based project similar to Outset (which runs boot and login scripts stored in various directories), and apparently there’s one already baked into macOS that will run daily, weekly, and monthly scripts.

Shoutout to @elios on the MacAdmins Slack for letting me know about periodic

Launch Daemons

If you run sudo launchctl list | grep periodic-, you’ll see that these launch daemons are running:

com.apple.periodic-monthly
com.apple.periodic-weekly
com.apple.periodic-daily

And, though I don’t love SIP in general, it’s great for this, because you can’t actually disable the launch daemons:

sudo launchctl unload /System/Library/LaunchDaemons/com.apple.periodic-daily.plist
/System/Library/LaunchDaemons/com.apple.periodic-daily.plist: Operation not permitted while System Integrity Protection is engaged

So that means as long as you can enforce your daily, weekly, and monthly scripts being in the right place, with the right permissions, and with the right hash, they’ll be run regularly-ish.

Locations of Scripts

You can find scripts in /etc/periodic/daily, /etc/periodic/weekly, and /etc/periodic/monthly. You can also put your own scripts in there (root-owned, 755 permissions), and they’ll run alongside the ones that come with macOS.

According to /etc/defaults/periodic.conf, though, there’s another recommended place to put scripts:

# periodic script dirs
local_periodic="/usr/local/etc/periodic"

So that would be /usr/local/etc/periodic/daily, /usr/local/etc/periodic/weekly, and /usr/local/etc/periodic/monthly. Having your scripts separated from the built-in scripts may be a good idea, even though they’ll run fine alongside the built-in scripts.

Logging

If your script has any echo commands, the output will go to the appropriate log file (by default, those logs would be /var/log/daily.out, /var/log/weekly.out, and /var/log/monthly.out), but there won’t necessarily (again, with the default settings) be any other indicators in the daily, weekly, and monthly logs that your scripts ran.

The format of the log seems to be a date/time, all the echo statements from the scripts run, and then a closer like -- End of daily/weekly/monthly output --.

Invoking manually

If you don’t want to wait until the next day, week, or month, you can do some manual testing by running a command like this, for example: sudo periodic daily

No TCC/PPPC support

Allowing full disk access to a script relies on giving that access to the actual parent process. As far as I can tell, the parent process is the /usr/sbin/periodic binary, but that binary (although shipped with macOS) isn’t code-signed.

Categories
Mac admin'ing

The limits of password-protecting a .mobileconfig profile

Three years ago, Rich Trouton wrote Adding password protection to manually installed management profiles, which gives step-by-step instructions for how to make a manually-installed profile prompt for a custom password (in addition to the local admin password) when being removed.

I’ve tested this on Catalina, and it still works!

That said, it worked only from the GUI (via System Preferences). If you remove the sample profile (sudo profiles remove -identifier 9f9a0b1f-7b17-4656-92aa-b7046ad88d00), it will just remove immediately with no custom password provided.

Your best bet for making a profile non-removable is to install it via MDM.

Categories
Mac admin'ing

Double-checking details of deployed PPPC/TCC profile from MDM

If you’ve deployed a PPPC/TCC profile from your user-approved MDM to a Mac, and you see the profile in System Preferences > Profiles, you can also verify all the details of the deployed profile on the Mac itself by going to /Library/Application Support/com.apple.TCC/MDMOverrides.plist (which is an SIP-protected directory, by the way).

Categories
Mac admin'ing

Things to keep in mind if using a profile to delay macOS updates

Now that Apple has removed the --ignore flag from softwareupdate, it’s recommending you use the forceDelayedSoftwareUpdates and enforcedSoftwareUpdateDelay flags (more details in Device Management Profile: Restrictions), which are supposed to, in theory, delay an updates user visibility a certain number of days after the update’s release.

The number of days delayed may not be precise

The number of days delayed is imperfect at best. I tested a 7-day delay and even 6 days after the release of the update, it was suddenly available. And here’s an example of someone last year who didn’t see an update released 10 days before, even with only a 7-day delay.

System Preferences and softwareupdate may not be in sync

In my own testing, if you delay an update but a Mac is two or more updates behind, softwareupdate -d -a or softwareupdate -l will still show an update available to download, but System Preferences > Software Update will show Your Mac is running the latest software update allowed by your administrator.

That can be a problem if you rely on a program like Nudge, which uses softwareupdate to determine whether an update is available but points users to System Preferences to do the actual update.

At this time, unless Apple makes significant changes, I wouldn’t recommend using the delay update profile settings if you have any utilities that use softwareupdate to check whether updates are available or not.

Categories
Mac admin'ing

Some basics of DEPNotify and a sample script

If you’ve been doing Munki admin’ing for a short while, you’ve probably heard people talk about DEPNotify, whose README says is “a small light weight notification app that was designed to let your users know what’s going on during a DEP enrollment.”

Aforementioned DEPNotify README is fairly comprehensive in terms of going over all the options. There’s also a project called DEPNotify-Starter, which has a sample script that’s, as of this writing, 827 lines long.

If you just want a super simple script to launch up DEPNotify and have it install some Munki stuff, I created a very, very simple sample script (DEPNotifyMunkiSample.sh) that just shows how you can use it without a ton of extra options. Once you wrap your head around that, you can always complicate it with more options.

This is what the sample script looks like in action if there’s only MunkiAdmin to install:

Categories
Mac admin'ing

If your VMWare guest macOS loses network connectivity

If you haven’t changed any settings, and suddenly the Internet connection on your VMWare guest macOS installation goes out, and shutting down the VM or rebooting the VM doesn’t help, try rebooting the host Mac. That will likely fix the problem (not sure why that problem comes up in the first place.

Categories
Mac admin'ing

Using a full macOS installer with Munki to patch macOS

Caveat

This works right now, but I don’t know if this will keep working in perpetuity. As Apple plans on removing enterprise options for macOS software update points out, being able to manage patches in macOS is a moving target, and Apple is increasingly making it difficult for administrators to manage patches effectively.

Shoutout

Shoutout to Rod Christiansen on the MacAdmins Slack for putting this strange (but still working for now) method of patching on my radar.

Why would you want to do this?

Recently, softwareupdate has become an increasingly unreliable way to install updates. Munki 5 recently brought in some changes to have Managed Software Center nudge users to install patches via System Preferences (more details at Manual Apple Updates in Munki 5), similar to what Nudge does.

That is still the most reliable way to get patches installed, but what if you have Macs that basically sit unattended, so Nudge or a nudge-like prompt from Managed Software Center will basically be useless?

Well, apparently, you can use the full macOS installer to automate patching.

Limitations and considerations

One huge limitation, of course, is that you can probably use this method to patch only the latest macOS release. In other words, as of this writing, the latest build available for macOS 10.14.6 is 18G5033, but the latest full downloader for 10.14.6 is build 18G103. So you can’t really use an 18G103 full installer to update a Mojave client to 18G5033.

I believe using the installer to patch is the equivalent of doing a non-destructive reinstall of macOS (which you usually do in recovery mode), which means the download is considerably bigger than a regular patch download (more like 8 GB instead of 1 GB), and the install time is also longer (35-60 minutes instead of 20-30 minutes).

If the machines you’re patching are FileVault encrypted, you’ll still have to have someone be present either before or after the full macOS install to either do an authorized restart beforehand or a manual unlocking of FileVault afterwards.

Tweaks to the pkginfo

When you import something like Install macOS Catalina.app into Munki, munkiimport will, by default, make the installer_type into startosinstall. You may still want that installer in your Munki repo so you can upgrade Mojave clients to Catalina, so keep that pkginfo, but also make a copy of that pkginfo and tweak things a bit.

Change the installer type to be a CopyFromDMG:
    <key>installer_type</key>
    <string>copy_from_dmg</string>
    <key>items_to_copy</key>
    <array>
        <dict>
            <key>destination_path</key>
            <string>/tmp</string>
            <key>source_item</key>
            <string>Install macOS Catalina.app</string>
        </dict>
    </array>

and then have a postinstall script that runs the startosinstall command after copying from the .dmg:     <key>postinstall_script</key>
    <string>#!/bin/zsh
# Run startosinstall
/tmp/Install\ macOS\ Catalina.app/Contents/Resources/startosinstall --agreetolicense
    </string>

You’ll also want to change the name, so it’s distinct from Install_macOS_Catalina:

    <key>name</key>
    <string>Update_macOS_Catalina</string>

Might as well update the display name as well:

    <key>display_name</key>
    <string>Update macOS Catalina</string>

The last important tweak is to throw an installcheck_script in there so Munki knows whether the patch is installed or not:     <key>installcheck_script</key>
    <string>#!/usr/local/munki/python
from distutils.version import LooseVersion
import subprocess
import sys
# Desired OS build
desired='19F101'
def main():
    # Get current OS build
    cmd = [ '/usr/bin/sw_vers', '-buildVersion' ]
    p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='utf8')
    out, err = p.communicate()
    if err:
        print("ERROR: Unable to determine current OS build. Considering installed for now...")
        sys.exit(1)
    else:
        # Strip out extra carriage return from output
        curr_build = out.strip()
        if LooseVersion(curr_build) &gt;= LooseVersion(desired):
            print("The current build {} is already greater than or equal to {}".format(curr_build, desired))
            sys.exit(1)
        else:
            print("Current build {} is not yet {}".format(curr_build, desired))
            sys.exit(0)
if __name__ == "__main__":
    main()
    </string>

Manifest Change

You may also want to add in a Munki conditional item so that only 10.15 clients are targeted.

    <key>conditional_items</key>
    <array>
        <dict>
            <key>condition</key>
            <string>os_vers_minor == 15</string>
            <key>managed_installs</key>
            <array>
                <string>Update_macOS_Catalina</string>
            </array>
        </dict>
    </array>

What the update process looks like

    Evaluating predicate: os_vers_minor == 15
    Predicate os_vers_minor == 15 is True
    * Processing manifest item Update_macOS_Catalina for install
    Looking for detail for: Update_macOS_Catalina, version latest...
    Considering 1 items with name Update_macOS_Catalina from catalog testing
    Considering item Update_macOS_Catalina, version 10.15.5.19F101 with minimum Munki version required 3.0.0.3211
    Our Munki version is 5.0.0.4034
    Considering item Update_macOS_Catalina, version 10.15.5.19F101 with minimum os version required 10.15
    Our OS version is 10.15.5
    Considering item Update_macOS_Catalina, version 10.15.5.19F101 with maximum os version supported 10.15.99
    Our OS version is 10.15.5
    Found Update_macOS_Catalina, version 10.15.5.19F101 in catalog testing
    Adding Update_macOS_Catalina to list of processed installs
    Running installcheck_script for Update_macOS_Catalina
    Current build 19F96 is not yet 19F101
    installcheck_script returned 0
    Need to install Update_macOS_Catalina

    The following items will be installed or upgraded:
        + Update_macOS_Catalina-10.15.5.19F101
            Updates macOS version to 10.15.5, build 19F101
         *Restart required

A key piece from /var/log/install.log: 2020-06-02 09:30:54-07 HOSTNAME softwareupdate[401]: Starting softwareupdate CLI tool
2020-06-02 09:30:54-07 HOSTNAME softwareupdated[402]: softwareupdated: Starting with build 10.15.5 (19F101)
2020-06-02 09:30:54-07 HOSTNAME softwareupdated[402]: authorizeWithEmptyAuthorizationForRights: Requesting provided rights: 1
2020-06-02 09:30:56-07 HOSTNAME softwareupdated[402]: /Library/Bundles does not exist - watching for directory creation
2020-06-02 09:30:56-07 HOSTNAME softwareupdated[402]: Previous System Version : 10.15.5 (19F96), Current System Version : 10.15.5 (19F101)

Yeah, that’s it. That whole install (not counting the download of the installer) took a full hour to complete. On another machine I tested with, it was closer to 35 minutes to complete. So, your mileage may vary.