Big Sur Kext Workflows

nstrauss
Contributor II

Hey education folks. Popping into the education community to once again shamelessly ask you to upvote my feature request. Maybe you're like me and there's still software using kernel extensions required in your environment. Well, that gets a bit tricky with Big Sur where kexts can't be installed without restarting.

Apple has given us a small reprieve with the RebuildKernelCache key recently included in a Big Sur beta. Apple says...

"If the AllowNonAdminUserApprovals key is absent or set to False, you can complete
installation of legacy kernel extensions either by using an administrator account to reboot your
Mac from within System Preferences > Security & Privacy, or by sending the the RestartDevice
MDM command with the RebuildKernelCache key set to True."

That means we can still automate deployment workflows including software with kexts as long as a MDM restart command with RebuildKernelCache is also sent. Maybe not exactly what I wanted, and not as seamless as previous workflows, but Apple's kextless future marches on. I'd rather not support kexts either. However, I'm in a spot where I do want to support Big Sur and also have a need to use kexts. I think a lot of education folks are in the same situation. If so, please upvote my feature request! Ask Jamf to support this key ASAP, and make sure it's possible to send through the API. Otherwise that whole pesky automation piece becomes not so automated.

https://www.jamf.com/jamf-nation/feature-requests/9814/rebuildkernelcache-with-restart-command

4 REPLIES 4

timmyp
New Contributor II

Jamf did a "partial implementation" of this last week. You need to call via the Jamf API. Anyone familiar with the Jamf API and how to call the restart with RebuildKernelCache command?

gragnarok
Release Candidate Programs Tester

I was able to get the below working today piecing together some stuff I've seen people do online and some fun python I wrote myself. You would need to replace the entries in the variables in the top with your information and I hard coded the JSS URL into the final API call because it was the end of the day and I didn't feel like figuring out which way I needed to quote it to make it work as a variable. 

Read through and get comfortable with what each step is doing before you use it, limitation that I don't currently have to deal with that you may is that this is only returning the first page of result for the computer records because I don't have enough devices currently for that to be a problem. You could add some logic in to handle searching through the other pages if that was something that you needed. Also, I am not specifying kext paths and have notifyUser set to false. 

Tested locally on my machine and pushing as a script from Jamf and it successfully rebooting the computers and when they came back up I verified that the KEXT I needed loaded was good to go.

 

 

#!/bin/bash

# server connection information
URL="JSS URL HERE"
username="API USERNAME HERE"
password="API USER PASSWORD HERE"

# create base64-encoded credentials
encodedCredentials=$( printf "$username:$password" | /usr/bin/iconv -t ISO-8859-1 | /usr/bin/base64 -i - )

# generate an auth token
authToken=$( /usr/bin/curl "$URL/api/v1/auth/token" \
--silent \
--request POST \
--header "Authorization: Basic $encodedCredentials")

#function for parsing json values
function jsonValue() {
KEY=$1
num=$2
awk -F"[,:}]" '{for(i=1;i<=NF;i++){if($i~/'$KEY'\042/){print $(i+1)}}}' | tr -d '"' | sed -n ${num}p
}

# parse authToken for token, omit expiration
token=$(echo $authToken | jsonValue token 1 )

# obtain Jamf computer records
computerList=$( /usr/bin/curl -X GET "$URL/api/preview/computers?page=0&size=100&pagesize=100&page-size=1000&sort=name%3Aasc" -H "accept: application/json" -H "Authorization: Bearer $token")

#collect computer serial number and output to file
serial=$(system_profiler SPHardwareDataType | awk '/Serial/ {print $4}')
echo $serial > /tmp/serial.txt

#search through results of computerList and find managementId of current computer
jamfID=$(echo $computerList | python -c "import sys, json, subprocess;
jsonSearch = json.load(sys.stdin);
i = 0;
with open('/tmp/serial.txt') as myfile:
	serial = ''.join(line.rstrip() for line in myfile);
loop = True;
while (loop):
	if serial == (jsonSearch['results'][i]['serialNumber']):
		loop = False
		print(jsonSearch['results'][i]['managementId']);
	else:
		i += 1;
	")

#Complete API command to reload KEXT cache on computer
/usr/bin/curl --location --request POST 'https://JSSURLHERE/api/preview/mdm/commands' \
--header "Authorization: Bearer $token" \
--header 'Content-Type: application/json' \
--data-raw '{
    "clientData": [
        {
            "managementId": "'$jamfID'",
            "clientType": "COMPUTER"
        }
    ],
    "commandData": {
        "commandType": "RESTART_DEVICE",
        "rebuildKernelCache": "true",
        "kextPaths": [
        ],
        "notifyUser": "false"
    }
}'

#remove temp serial file
rm -rf /tmp/serial.txt

# expire the auth token
/usr/bin/curl "$URL/api/v1/auth/invalidate-token" \
--silent \
--request POST \
--header "Authorization: Bearer $token"

 

 

rjashton
New Contributor II

Thanks for sharing this, it has been really helpful for me creating our own solution. I've split it up a bit - I'm saving the managementID into a plist on the Mac with a policy that runs on check-in/enrolment. This is so that it can be used by other scripts when the need arises. I then have a second script that does the MDM restart via self service when required. 

gragnarok
Release Candidate Programs Tester

No problem. Love the idea of storing locally in a plist, much easier than calling the API every time, I'll definitely steal that idea from you. That being said, Jamf did implement KEXT loading into the restart policy action so my script isn't necessarily needed anymore.