Command-Control-Power

šŸ”’ Patreon Special

IT Pros: exclusive shows await you on Patreon, focusing on the more challenging aspects of running your practice and working with clients and employees.


Unable to Verify Message Signature in Mail.app

Fact #1: When you send an email from Mail.app, if you have a certificate in your keychain that matches the ā€œfromā€ email address, that certificate is used to sign the message.

Fact #2: When Mail.app receives a signed email message, it adds the sender’s attached certificate to the user’s keychain.

So far so good. But there’s an issue that can manifest as a result of those two facts.

If you send yourself a signed email from Mail.app, Mail treats the incoming message just like any received email in Fact #2 above.

That becomes an issue if the message in question was improperly signed with an invalid certificate. Mail sees the received email’s certificate and adds it to your keychain. Then, when you go to send the next email, Mail sees the certificate in your keychain and uses it to sign the new outgoing email.

If the signature is invalid, the recipient sees a banner at the top of the received message:

The solution is much trickier than one might think…

Read More

Configure Down Alerts for UniFi networks

UniFi has become an industry standard network platform and a popular choice among Apple Consultants. Surely you’ve heard us discuss our UniFi experiences on the podcast. In fact, we discussed the topic of down alerts for UniFi networks on episode #538: Welcome All Pings.

Why is this necessary?

UniFi offers solid options for built-in push notifications delivered to your mobile device or email, including for important events like when your network switches to backup internet or backup power, hits an LTE Data Limit, or detects a rogue client or access point for example.

But one very obvious omission is the ability to alert an admin when the network goes down. This prompts an equally obvious question: how is the network supposed to alert you when it’s down – when it’s down? That’s a fair question for a locally-hosted controller, but with Remote Access enabled, this shouldn’t be an issue since the UniFi controller shows in the UniFi Site Manager at unifi.ui.com. In fact, the Site Manager will indicate if a network is down and when last reported online. So why doesn’t it have an option to alert the admin?

In this post, I’ll provide instructions on how to configure down alerts to be sent by push notification and/or email when your UniFi network goes offline, using NameCheap for Dynamic DNS and UptimeRobot for monitoring.

Read on for the full instructions…

Read More

Scheduled macOS software updates using MDM with good user communication

Skip to the end if you want to see the Instructions and Code…

Background

Apple’s Startup Disk security policy control for a Mac with Apple silicon article states that ā€œremote management of […] automatic software updatesā€ requires setting Security Policy to Reduced Security in Startup Security Utility using recoveryOS.

Addigy’s System Updates via MDM and DDM article explicitly states that Apple Silicon Macs require either ADE or Reduced Security Mode to allow System Updates via MDM or DDM.

Therefore, I haven’t relied on System Updates via MDM and DDM because most of my client Macs are user-enrolled (non-ADE), and I don’t want to walk individual users through booting to Recovery mode – which would be onerous at scale. And asking users to choose an option clearly labeled ā€œReduced Securityā€ would prompt a nuanced discussion with certain clients about why we’re asking them to ā€œreduceā€ their security when in fact our intention is to increase their security.

However, in my experience, MDM software updates seem to work fine – even for Macs that were not enrolled using ADE, and where you don’t need to have the client reboot into Recovery mode and change to Reduced Security.

The old method

I recently decided to learn more about System Updates via MDM and thoroughly test these workflows since my usual method relying on Nudge and the openToMoreInfo GUI scripting AppleScript app stopped working reliably. The first issue is that Nudge relies on the user navigating the Software Update window, skipping past the Sonoma Upgrade to find the ā€œMore Infoā€ button under ā€œOther updates availableā€ subheading, then clicking Install Now, and Agree. Too many steps!

openToMoreInfo solved this fairly elegantly for awhile, but in Ventura and Sonoma, Apple made a change to the underlying GUI elements making the More Info ā€œbuttonā€ into a static text element which doesn’t respond to a GUI scripting ā€œclickā€ command, and appears to be specifically blocked from any kind of ā€œsynthetic clickā€. So we’re back to only being able to provide guidance to the user, telling them where to look and what to click. Not ideal.

That was less of an issue when we were able to defer the major upgrade using an MDM configuration profile. This would hide Sonoma from appearing in Software Update, so the only updates listed would be the minor updates we wanted the user to install. However, the maximum deferral is 90 days – which, based on the Sonoma release date, happens to be Christmas Day.

Since we can’t programmatically guide the user past the tempting Sonoma Upgrade with its pretty icon, and since Nudge inherently relies on using the Software Update window, this began to cause more problems than it solved. We were actively directing users into Software Update, making it more likely that they would begin an upgrade to Sonoma before we were ready to support it.

The new method

The new method leverages scheduled MDM software updates rather than using Nudge to guide the user to the Software Update window and seems to work well despite Apple’s guidance noted above.

I tested this on several user-enrolled (non-ADE) Macs with Full Security, and I was able to successfully push updates via MDM using GoLive > Updates > Download and/or Install:

  • Ventura 13.2 (VM): Updated to 13.6.3.

  • Ventura 13.6.1 (iMac 24-inch M1): Updated to 13.6.3. First prompt was awaiting user confirmation of Restart. Declined first restart prompt as a test, sent Download and/or Install again, saw countdown, let it restart on its own.

I also tested a few using using Policy > Updates > System Updates > Enable macOS Updates > Schedule Updates:

  • Monterey 12.6.3 (VM): Updated to 12.7.2.

  • Monterey 12.7.1 (iMac Retina 4K, 21.5-inch, 2019) – Mac at login window, no user logged in: Updated to 12.7.2.

  • Ventura 13.6.1 (iMac 24-inch M1) - Mac at Lock Screen with user logged in: Updated to 13.6.3.

  • Ventura 13.6.1 (MacBook Pro 13-inch M1) - MacBook Pro with user logged in, lid closed, with power adapter connected: Updated to 13.6.3.

So we have a functional workflow using scheduled macOS software updates via MDM, allowing Macs to update with minimal user interaction.

How to use the new method

Simply enable macOS Updates, set a maximum version, and schedule accordingly. See Addigy’s System Updates via MDM and DDM article for more instruction about how to enable and schedule this method.

User communication

What are some reasons that a Mac could fail to update using this method?

A couple of issues we can foresee would be when a laptop isn’t connected to power during the scheduled update window, or when a Mac doesn’t have enough space available to update.

We can handle those potential issues with good user communication:

Instructions:

  1. Create a Flex Policy, e.g. ā€œmacOS Software Updates Tuesday 7pmā€ and follow Addigy’s guidance to enable Scheduled System Updates via MDM and DDM. Specify a long enough scheduled update window, e.g. 4 hours.

  2. Create a Custom Fact in Addigy > Catalog called ā€œSoftware Update Neededā€ of type Boolean. This reports TRUE if the current macOS version is less than the latest macOS update e.g. running 13.6.1 when 13.6.3 is released, and also saves the value (TRUE/FALSE) to a file on the Mac which the Scripts below can reference.

  3. Create a Script in Devices > Scripts > Manage called ā€œLaptop on Battery Update Promptā€. This checks if Software Update Needed, and if so, checks if laptop is on battery, and prompts the user.

  4. Create a Script in Devices > Scripts > Manage called ā€œLow Space Update Promptā€. This checks if Software Update Needed, and if so, checks if free space > 25GB, otherwise prompts the user.

  5. Create a Maintenance Item in Addigy > Catalog called ā€œUpdate Checks 5pm Tuesdayā€, include both scripts above, and schedule accordingly.

  6. Add your Software Update Needed Custom Fact and Update Checks 5pm Tuesday Maintenance Item to your ā€œmacOS Software Updates Tuesday 7pmā€ Flex Policy.

  7. Optional: enable Microsoft Office updates scheduled for 5:15pm in the same Flex Policy – might as well prompt the user to quit Office apps if needed.

  8. Assign your Flex Policy to some test Macs, or use auto-assignment to add your Flex Policy to some of your existing test policies. Deploy the policy to ensure the Macs check in and run the Software Update Needed Custom Fact before the Maintenance Item runs your scripts ahead of the scheduled Software Update window. Adjust the schedules accordingly for testing purposes.

Code for Custom Fact & Scripts

Software Update Needed – Custom Fact
Based on work by Ross Matsuda | Sudoade 2023

#!/bin/bash

# by Joe Saponare, CommandControlPower.com: https://commandcontrolpower.com/podcast/2023/12/16/scheduled-macos-software-updates-using-mdm-with-good-user-communication

software_update_needed_file="/Library/Addigy/software_update_needed.txt"

### Define latest macOS versions
# Get list of OS versions
softwareupdatelist="$(curl -m 5 -sfL 'https://gdmf.apple.com/v2/pmv')"
# echo "$softwareupdatelist"

### Truncate list - leaving comments to explain function of each section
macOSonly=${softwareupdatelist#*macOS} #Strip out all content before first mention of "macOS"
  # macOStruncated="$(echo "$macOSonly" | tr , '\n' | grep "ProductVersion")" # Use commas for line breaks, remove all lines other than ProductVersion entries
  # macOStruncated1="$(echo "$macOStruncated" | sed '/iOS/q' | sed '$d')" #Remove all content after macOS versions listed
  # macOStruncated2="$(echo "$macOStruncated1" | awk -FProductVersion '{print $2}')" #Remove extraneous text
  # macOStruncated3="$(echo "$macOStruncated2" | sed 's/"//g' | sed 's/://g')" #Remove extraneous punctuation
speedTruncation="$(echo "$macOSonly" | tr , '\n' | grep "ProductVersion" | sed '/iOS/q' | sed '$d' | awk -FProductVersion '{print $2}' | sed 's/"//g' | sed 's/://g')"
 #echo " speed truncation output: $speedTruncation"

### Version Extractions
latest11="$(echo $speedTruncation | tr ' ' '\n' | grep 11)"
#echo "Big Sur: $latest11"
latest12="$(echo $speedTruncation | tr ' ' '\n' | grep 12)"
#echo "Monterey: $latest12"
latest13="$(echo $speedTruncation | tr ' ' '\n' | grep 13)"
#echo "Ventura: $latest13"
latest14="$(echo $speedTruncation | tr ' ' '\n' | grep 14)"
#echo "Ventura: $latest14"

# Get current OS
currentOS=$(sw_vers -productVersion)
currentOSMajor=$(echo $currentOS | awk -F. '{print $1}') # outputs 10, 11, 12, 13, 14

software_update_needed=""

if [[ $currentOSMajor = 14 ]]; then
  # macOS Sonoma (14) detected
  if [[ $currentOS = $latest14 ]]; then
    software_update_needed="FALSE"
  else
      software_update_needed="TRUE"
  fi
elif [[ $currentOSMajor = 13 ]]; then
  # macOS Ventura (13) detected
  if [[ $currentOS = $latest13 ]]; then
    software_update_needed="FALSE"
  else
      software_update_needed="TRUE"
  fi
elif [[ $currentOSMajor = 12 ]]; then
  # macOS Monterey (12) detected
  if [[ $currentOS = $latest12 ]]; then
    software_update_needed="FALSE"
  else
      software_update_needed="TRUE"
  fi
elif [[ $currentOSMajor = 11 ]]; then
  # macOS Monterey (11) detected
  if [[ $currentOS = $latest11 ]]; then
    software_update_needed="FALSE"
  else
      software_update_needed="TRUE"
  fi
else
  # if the system is not running macOS 11, 12, 13, or 14, then we assume no updates are needed
  software_update_needed="FALSE"
fi

echo $software_update_needed
echo $software_update_needed > $software_update_needed_file
exit 0

Laptop on Battery Update Prompt – Device Script

#!/bin/bash

# by Joe Saponare, CommandControlPower.com: https://commandcontrolpower.com/podcast/2023/12/16/scheduled-macos-software-updates-using-mdm-with-good-user-communication
# Known Limitations: no known limitations
# Requires Custom Fact "Software Update Needed" as noted in link above.

software_update_needed_file="/Library/Addigy/software_update_needed.txt"
software_update_needed=$(cat $software_update_needed_file)

#for testing:
#software_update_needed="TRUE"

[[ $(pmset -g ps|grep "AC Power") ]] && state="AC" || state="BATT"

model=$(sh /Library/Addigy/auditor-facts/scripts/device_model_name)

forefront="True"
title=$"🪫 Please connect to power"
description=$"Your ${model} needs to be connected to power to install scheduled updates.

Please connect your power adapter (charger) and leave your ${model} screen open."

# Get logged in user.
username=$(stat -f %Su /dev/console)

if [[ $software_update_needed == "TRUE" ]]; then
  echo "Software update needed: $software_update_needed."
    
    if [ "$state" == "BATT" ]; then
        echo "$model is on battery."

        if [ "$username" != "root" ]; then
        echo "User $username is logged in. Prompting user."

    if /Library/Addigy/macmanage/MacManage.app/Contents/MacOS/MacManage action=notify title="${title}" description="${description}" forefront="$forefront"; then
    echo "User $username clicked OK."
    exit 0
    fi
        else
        echo "No user is logged in. Unable to display prompt."
        fi
    elif [ "$state" == "AC" ]; then
        echo "$model is on AC power."
    fi
else
    echo "Software update needed: $software_update_needed."
fi

Low Space Update Prompt – Device Script

#!/bin/bash

# by Joe Saponare, CommandControlPower.com: https://commandcontrolpower.com/podcast/2023/12/16/scheduled-macos-software-updates-using-mdm-with-good-user-communication
# Known Limitations: no known limitations
# Requires Custom Fact "Software Update Needed" as noted in link above.

software_update_needed_file="/Library/Addigy/software_update_needed.txt"
software_update_needed=$(cat $software_update_needed_file)

#for testing:
#software_update_needed="TRUE"

model=$(sh /Library/Addigy/auditor-facts/scripts/device_model_name)
free_space=$(sh /Library/Addigy/auditor-facts/scripts/free_disk_space_gb)

#for testing:
#free_space=15

space_needed="30"

forefront="True"
title=$"šŸ”» Need more space to update"
description=$"Your ${model} needs more space to install scheduled updates.

Please free up at least ${space_needed} GB or contact us for help."

# Get logged in user.
username=$(stat -f %Su /dev/console)

if [[ $software_update_needed == "TRUE" ]]; then
    echo "Software update needed: $software_update_needed."
    if [ "$free_space" -le "$space_needed" ]; then
        echo "$model has $free_space GB which is less than $space_needed GB space needed."

        if [ "$username" != "root" ]; then
            echo "User $username is logged in. Prompting user."
            if /Library/Addigy/macmanage/MacManage.app/Contents/MacOS/MacManage action=notify title="${title}" description="${description}" forefront="$forefront"; then
                echo "User $username clicked OK."
                exit 0
            fi
        else
            echo "No user is logged in. Unable to display prompt."
        fi
    else
        echo "$model has $free_space GB free space. (Minimum $space_needed GB needed.)"
    fi
else
    echo "Software update needed: $software_update_needed."
fi

Taking a Stand

Command-Control-Power stands against the systemic racism and prejudice that pervades the country and society we live in. America has a long and painful history of violence perpetrated against black people. As allies of the Black Lives Matter movement, we want to be active participants in bringing a long-overdue end to this cycle of oppression.

We are inspired by our colleagues and those in other industries who have chosen to speak out, both personally and professionally against ongoing social injustice and inequality. On a daily basis, we see hateful rhetoric, ever expanding ignorance, and continuing violence. Recent events have put these weaknesses of our society on full display. We must try to return to a time of meaningful discourse. If our country’s leaders are incapable of leading by example, we need to step up individually to show our peers how it is done.

Those of us who have privately disapproved of the shameful behavior and injustices to our fellow humans should no longer stand silent. We must peacefully find ways to not only express our disapproval but take action to create change.

There is good in this world. Not all who are sworn to protect and serve are evil. Not all who are of opposing viewpoints are ignorant. But enough is happening in public view to show all of us that action towards meaningful change is needed. That can begin by making your voice heard. By donating to a cause. By cleaning up your city’s streets. Show the world that you care and others can follow in your footsteps.

We have donated one month's Patreon income from Command Control Power to these three charities: Black Lives MatterCommunities United Against Police Brutality, and Campaign Zero.

Please consider joining us in donating to a cause. Here are some worthwhile resources, if you so choose.

To listen is to learn. To that end, if you have any input or thoughts you would like to share with us, we would love to hear from you.


Sam, Joe & Jerry
Command Control Power