Tiny Deathstars of Foulness

The videos from the MacADUK sessions are now available on the Internets! Including such great sessions as “What’s New With Managing macOS and iOS” from Marko Jung, “Something something commercial, something something open source” from Graham Gilbert, “Desired State Management Through Automation with Jamf Pro” from John Kitzmiller, “Advanced Mac Software Deployment and Configuration – Just Make it Work” from Tim Sutton, “Securing the Managed Environment – you, me, and everybody” from Pepijn Bruienne, “Munki and Patch. A Comparison” from Ben Toms & James Ridsdale, “Locking down macOS without Locking Up Users (The Sequel)” from Samuel Keeley. Totes fun!

Watch them at

March 28th, 2017

Posted In: Mac OS X, Mac OS X Server, Mac Security

Tags: , , , , ,

March 8th, 2017

Posted In: Mac OS X Server, Mac Security, MacAdmins Podcast

One of my favorite things about grabbing things with scripts is just how many ways (and sometimes how needfully or needlessly convoluted you can make them) to grab the same pieces of information. For example, something as simple as what hosts you use to resolve names on a Mac. There are a number of ways to grab what DNS server a device is using in macOS. So when you’re running a script you might choose to grab DNS information one way or another, according to what you’re after. Some of this might seem more complicated than it should be. And that’s correct…


The /etc/resolv.conf file is updated automatically to look at what servers are used to resolve names used for DNS. The easiest way to see theses to simply cat it and grep for nameserver:

cat /etc/resolv.conf | grep nameserver


The next way we’ll grab DNS information is using scutil. Here, we use the –dns option, which outputs a lot of DNS stuffs, including all the built-in resolvers:

scutil --dns

To just grab the name servers:

scutil --dns | grep nameserver

We can also simplify the output to just the servers with awk:

scutil --dns | grep nameserver | awk '{print$3}'


The second way is using networksetup. This command has an option to get a DNS server in (shocker) -getdnsservers. However, you have to list the interface for each. So below we’ll dump all interfaces into an array using -listallhardwareports and then read them in using a for loop and querying the name servers.

interfaces=( "$(networksetup -listallhardwareports | grep Hardware | cut -c 16-900)" )
for i in "${interfaces[@]}"
networksetup -getdnsservers $i

The one tricky thing in this one is I initially forgot to quote the interfaces as they went into the array, which meant each word of the interface was an item in the array and therefore the -getdnsservers option failed. Once I quoted, it was all happy. The other thing I can point out is I used cut instead of sed because it was easier to quote; however, it seems unlikely the name can be more than 890 characters, so I think it’s fine…


You can also use dig. Here, you’ll query for a name without using an @ option, but omit everything but the line with the server that responded:

dig | grep SERVER:

The output is kinda’ fug:


For simpler output, we’ll use sed to constrain the output to just what’s between the parenthesis:

dig | grep SERVER: | sed 's/^.*(//;s/)$//'


nslookup is a tool similar to dig, used for querying names. We’ll basically do the same thing as above, just using awk as it’s just a standard position in a line:

nslookup | grep Server: | awk '{print$2}'


Then there’s system_profiler, the command line interface for System Profiler. Here, we can query the SPNetworkDataType. This is going to produce a lot of output, so we can limit it to just the DNS servers using grep to constrain to just the lines we want and awk for just the columns in those lines, as follows:

system_profiler SPNetworkDataType | grep "Domain Name Servers:" | awk '{print$4}'


@knapjack added to use hosts. I had to use verbose mode to pull the local name server as follows:

host -v -t ns | grep Received | awk '{print $5}'


Thanks to the lovely Allister (@sacrilicious), we also have ipconfig to add to the list:

/usr/sbin/ipconfig getpacket en0 2> /dev/null | grep name_ | cut -d' ' -f3-

There are tons of ways to find things in macOS. Do you have a way to find a DNS server that I didn’t think of here?

March 6th, 2017

Posted In: bash, Mac OS X, Mac OS X Server, Mac Security, Ubuntu

Tags: , , , , , , ,

According to @johnkitzmiller, you can’t spell function without fun. So let’s have some fun! What’s a function? Think of it as a script inside a script. Define functions at the beginning of the script instead of making repeated calls to the same task within a script. The other nice thing about functions is that the act of compartmentalization makes them simple to insert into a number of different scripts. For example, if you do a lot of curl commands to pull down something in a lot of different scripts, having the grabbing of the data as a function, then the parsing of it into an array as a function and ultimately the writing of it or dealing with an stderr as another might make it simpler to then port it into the next script and the next.

Functions are simple to define. Just use (yes, you guessed it) the function command. So let’s look at the most basic function. Here, we’ll wrap a simple echo line inside curly brackets. So the syntax is function followed by the name of the function, followed by a curly bracket to introduce it. Then, I like to put a curly bracket on a line at the end of the function. Then I have a line where I just call the function. Note, there’s no special indicator, like a $ in front of the name of it or anything like that (unless you maybe variabalized it):

function hellokitzy {
echo "Hello Kitzy"

OK, so when you call it, it says hellokitzy. Obviously it could have nested if/thens, whiles, cases, etc. Now, let’s have two functions. In this example, we’ll basically just split the single echo statement into two; then call them in separate lines:

function hello {
echo "Hello"
function kitzy {
echo "Kitzy"

As with shell scripts, you can also push a positional parameter into the function. Here, we pass a positional parameter into the script and it echos a hello to that parameter. You know, making our scripts a bit more personal and all… Then we call the function twice. In the first instance, we just pass the same parameter, but in the second, we actually replace it. We do this to show that the function overwrites the $1 inside that function, but if we did another call to the function we’d just get the original $1 as it doesn’t persist outside of the function:

function term {
function hello {
echo "Hello" $1
hello $1
hello all
echo "bye"

When run with a parameter of Kitzy, the above would simply output:

Hello Kitzy
Hello all

That’s just for positional parameters that you’re feeding into a script though. If you have a variable (let’s call it a) and you update it in a function, then it will be the updated variable after the function. So in the following example, a echos out as two in the end:

function quit {
echo $a

Overall, functions are easy to use and make your code more modular. The only things that get a little complicated is that unless you know functions, you aren’t sure what’s going on in the beginning and when you are editing variables throughout the script you wanna’ make sure you know what changed things and when.

OK, now you – have fun with functions, and feel free to use the comments to post some you wrote!

February 28th, 2017

Posted In: Mac OS X, Mac OS X Server, Mac Security, Unix

Tags: , , , ,

To tell curl that you can read and write cookies, first we’ll start the engine using an empty cookie jar, using the -b option, which always reads cookies into memory:

curl -b newcookiejar

If your site can set cookies you can then read them with the -L option

curl -L -b newcookiejar

The response should be similar to the following:

Reading cookies from file

Curl also supports reading cookies in from the Netscape cookie format, used by defining a cookies.txt file instead:

curl -L -b cookies.txt

If the server updates the cookies in a response, curl would update that cookie in memory but unless you write something that looks for a new cookie, the next use will read the original cookie again.

To create that file, use the -c option (short for –cookie-jar) as follows:

curl -c cookie-jar.txt

This will save save all types of cookies (including session cookies). To differentiate, curl supports junk, or session cookies using the –junk-session-cookies options, or -j for short. The following can read these expiring cookies:

curl -j -b cookie-jar.txt

Use that to start a session and then that same -c to call them on your next use. This could be as simple as the following:

$CURL -j -b $COOKIEJAR $site

You can also add a username and password to the initial request and then store the cookie. This type of authentication and session management is used frequently, for example in the Munkireport API, as you can see here:

For converting, the -b detects if a file is a Netscape formatted cookie file, parses, then rewrites using the -c option at the end of a line:

curl -b cookie.txt -c cookie-jar.txt

February 20th, 2017

Posted In: Mac OS X, Mac Security

Tags: , , , , , ,

You can quickly and easily back up your Filewave databases using the fwcontrol command to stop a Filewave server (thus preserving the integrity of the data you are backing up) and then backing up the database using the /fwxserver directory.

To get started, we’ll first down the server. This is done using the fwcontrol command along with the server option and the stop verb, as follows:

sudo fwcontrol server stop

Now that there won’t be data trying to commit into the database, let’s make a backup of the database directory using the cp command:

cp -rp /fwxserver/DB ~/Desktop/Databasebak

To start the database, use the decontrol command with the server option and the start verb, as follows:

fwcontrol server start

Note, if you will be moving to a new Filewave server, you would want to lock clients during this transition, so before restarting your server, use the sqlite3 command to set the status to 1 in the user table:

sqlite3 /fwxserver/DB/server.sqlite 'update user set status = 1;'

February 15th, 2017

Posted In: Mac OS X, Mac OS X Server, Mac Security, Network Infrastructure

Tags: ,

As promised, here’s the slide deck from my talk at MacAD.UK in London. Enjoy!


February 7th, 2017

Posted In: Mac OS X, Mac OS X Server, Mac Security, Mass Deployment

When you’re regression testing, you frequently just don’t want any delays for scripts unless you intentionally sleep your scripts. By default Safari has an internal delay that I’d totally forgotten about. So if your GUI scripts (yes, I know, yuck) are taking too long to run, check this out and see if it helps:

defaults write WebKitInitialTimedLayoutDelay 0

With a script I was recently working on, this made the thing take about an hour less. Might help for your stuffs, might not.

If not, to undo:

defaults delete WebKitInitialTimedLayoutDelay


February 1st, 2017

Posted In: Mac OS X Server, Mac Security

Tags: , , , , , , , ,

The “What’s New in macOS” page for Sierra (10.12) lays out a little known change that a colleague at Jamf was working on the other day (hat tip to Brock):

Starting in macOS 10.12, you can no longer provide external code or data alongside your code-signed app in a zip archive or unsigned disk image. An app distributed outside the Mac App Store runs from a randomized path when it is launched and so cannot access such external resources. To provide secure execution, code sign your disk image itself using the codesign tool, or distribute your app through the Mac App Store. For more information, see the updated revision to macOS Code Signing In Depth.

This is further explained in the equally misnamed “OS X Code Signing In Depth“:

If using a disk image to ship an app, users should drag the app from the image to its desired installation location (usually /Applications) before launching it. This also applies to apps installed via ZIP or other archive formats or apps downloaded to the Downloads directory: ask the user to drag the app to /Applications and launch it from there.

This practice avoids an attack where a validly signed app launched from a disk image, ZIP archive, or ISO (CD/DVD) image can load malicious code or content from untrusted locations on the same image or archive. Starting with macOS Sierra, running a newly-downloaded app from a disk image, archive, or the Downloads directory will cause Gatekeeper to isolate that app at a unspecified read-only location in the filesystem. This will prevent the app from accessing code or content using relative paths.

The gist is, if an app isn’t signed via the Mac App Store, Gatekeeper is going to limit the ability of the app to launch via “Gatekeeper Path Randomization.” Basically, treat an app from a mounted drive as if it were coming from a Safari download. There are a few ways to distribute app bundles or binaries that do not violate this. One is to sign a disk image that contains such an app:

spctl -a -t open --context context:primary-signature -v /Volumes/MyApp/MyApp.dmg

If spctl runs properly, you should see the following:

/Volumes/MyApp/MyAppImage.dmg: accepted source=mydeveloperid

In the above spctl command, we use the following options:

  • -a assesses the file you indicate (basically required for this operation)
  • -t allows me to specify a type of execution to allow, in this case it’s ‘open’
  • –context
  • -v run verbosely so I can build error correction into any scripts
  • –status while I don’t use status, I could do a second operation to validate that the first worked and use the status option to check it
  • –remove I also don’t use remove, but I could undo what I just did by doing so (or just deleting the dmg

For more on managing Gatekeeper from the command line, see

Another method is to remove the lsquarantine attribute, which is automagically applied, using xattr as follows:

xattr -r -d /Volumes/MyApp/

The options in the above use of the xattr command:

  • -r run recursively so we catch binaries inside the app bundle
  • -d delete the bit

Xattr has a lot of different uses; you can programmatically manage Finder tags with it, To see the full xattr dump on a given file, use the -l option as follows:

xattr -l MyAppImage.dmg

The output is as follows:

xattr: No such file:
00000000 62 70 6C 69 73 74 30 30 A1 01 33 41 BE 31 0B A5 |bplist00..3A.1..|
00000010 70 D4 56 08 0A 00 00 00 00 00 00 01 01 00 00 00 |p.V………….|
00000020 00 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 |…………….|
00000030 00 00 00 00 13 |…..|
00000000 62 70 6C 69 73 74 30 30 A1 01 5F 10 22 63 69 64 |bplist00.._.”cid|
00000010 3A 69 6D 61 67 65 30 30 31 2E 70 6E 67 40 30 31 |:myappimage.dmg@01|
00000020 44 32 36 46 46 44 2E 35 37 31 30 37 30 46 30 08 |D26FFD.571070F0.|
00000030 0A 00 00 00 00 00 00 01 01 00 00 00 00 00 00 00 |…………….|
00000040 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |…………….|
00000050 2F |/|

This could be helpful when troubleshooting and/or scripting (or just way too much informations!).

Finally, if you’re an application developer, check out new API for App Translocation in the 10.12 SDK for <Security/SecTranslocate.h>  I guess one way to think of this is… Apple doesn’t want you running software this way any more. And traditionally they lock things down further, not less, so probably best to find alternatives to running apps out of images, from a strategy standpoint.

January 25th, 2017

Posted In: Mac OS X, Mac Security

Tags: , , , , ,

Built a quick extension attribute for Jamf Pro environments to check if TouchID is enabled and report back a string in $result – this could easily be modified and so I commented a few pointers for environments that might need to modify it (e.g. to check for user-level as it’s currently system-level). To see/have the code, check

January 18th, 2017

Posted In: JAMF, Mac Security

Tags: , , , , , , ,

« Previous PageNext Page »