Time to get your hands dirty :smile:

In Part 2, you will learn how to create a signing certificate for your packages, sign a basic munki package for deployment and finally create a custom munki package with some defaults.


If you are reading this, I am going to make a few assumptions:

  1. You are already using munki in production or have used munki in the past.
  2. You have deployed a package in some capacity
  3. You understand the basics of git

Tools Needed

Obtaining your certificates

Step 1: Creating and downloading your Apple Developer Signing Certificate

  1. Login to https://developer.apple.com/account/

  2. Under Certificates, Identifiers & Profiles, select OS X cert_signing_1.png

  3. Click on the + sign to begin the process of creating a new certificate. cert_signing_2.png

  4. Under Production select Developer ID cert_signing_3.png

  5. Download the two Apple Developer certificates. These will be used later. cert_signing_4.png

  6. Select Developer ID Installer for the type cert_signing_5.png

  7. You will be asked to generate a CSR cert_signing_6.png

  8. Open /Applications/Utilities/Keychain Access.app

  9. Under the Menu bar, select Certificate Assistant -> Request a Certificate From a Certificate Authority cert_signing_7.png

  10. Fill in the User Email Address and Common Name
  11. Save the CSR to disk. cert_signing_9.png

  12. Upload the CSR to the Apple Developer Portal cert_signing_10.png cert_signing_11.png

  13. If done correctly, you should now have access to your signing certificate cert_signing_12.png

Step 2: Installing Signing Certificate and Apple Developer Certificate Authorities

  1. In Keychain Access, Open the System keychain and select Certificates

  2. Drag and drop the two Apple Certificate Authorities into the keychain.
    • AppleWWDRCA.cer
    • DeveloperIDCA.cer cert_install_1.png
  3. You will be asked to validate the installation of the certificates. Accept and authenticate if needed. cert_install_2.png

  4. In Keychain Access, Open the Login keychain and select Certificates

  5. Drag and drop your newly created signing certificate into the keychain. The file should be called developerID_installer.cer. Validate/authenticate the installation if needed.

  6. If done correctly, you should now see both the public and private key of your Developer certificate. cert_install_3.png

  7. Write down the name of your Certificate as this will be used later. It will be in the form of Developer ID Installer: Name (Identifier) cert_install_4.png

Munki - Standard Deployment

Creating the Package

  1. Open up terminal.

  2. Create a working directory.
    • mkdir -p ~/DEP
  3. Inside of the working directory, clone the munki repository via git.
    • cd ~/DEP
    • git clone https://github.com/munki/munki.git
  4. Go to the newly create munki directory.
    • cd ./munki
  5. Run the new make_munki_mpkg_DEP script with the -s flag. You will pass your developer certificate as a string. This requires elevated permissions to run.
    • sudo ./code/tools/make_munki_mpkg_DEP.sh -s "Developer ID Installer: Example (R9UM25C6B5)"

If everything goes right you should see the following in Terminal:

productbuild: Using timestamp authority for signature
productbuild: Signing product with identity "Developer ID Installer: Example (R9UM25C6B5)" from keychain /Users/Example/Library/Keychains/login.keychain-db
productbuild: Adding certificate "Developer ID Certification Authority"
productbuild: Adding certificate "Apple Root CA"
productbuild: Wrote product to /Users/Example/DEP/munki/munkitools-
Distribution package created at /Users/Example/DEP/munki/munkitools-

Removing temporary files...

You can validate this with Suspicious Package munki_signed_1.png

Great. So now you have a signed munki package with your developer certificate, but in order to utilize InstallApplication you must create a Manifest.plist that tells your MDM how to install the package.

Depending on your vendor, this process may be automated, but for demonstration purposes, we will use Victor’s appmanifest tool.

Creating the Manifest.plist

  1. Using a browser, download appmanifest. Save this to the root of your working DEP folder.

  2. In Terminal, go back to your root DEP folder
    • cd ~/DEP
  3. Mark the Appmanifest binary as executable
    • chmod a+x ./appmanifest
  4. Run appmanifest and point it to your newly created munki package.
    • ./appmanifest ./munki/munkitools-
    • appmanifest should print out an XML file:
    • You can directly write your file: ./appmanifest ./munki/munkitools- > ./Manifest.plist

The appmanifest binary will write a url key. This is where you can specify the https:// url that your package will be located. Depending on your vendor, this process may be automated or you will be able to upload this file yourself, but for demonstration purposes, we will fill it in with an example url.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">

Now that you have your signed package and corresponding manifest.plist, you can now deploy munki via your MDM server!

Let’s look at that process now (below is a video, not a picture).

DEP YouTube video

If you pay close attention, as the device was scrolling down the /Applications list, Managed Software Center finished installing.

And this is great as a Proof of Concept, but what if you actually need to configure some defaults for Munki (say https)?

Munki - Custom Deployment

For this next example, we are going to do the following:

  • Expand our munkitools- Distribution package
  • We are going to create a simple flat package with munkipkg that will use the defaults command to set our url
  • recreate the package, inserting our new package.

Expanding our signed distribution package

  1. In Terminal, go back to your root DEP folder
    • cd ~/DEP
  2. Expand the munkitools package.
    • /usr/sbin/pkgutil --expand ./munki/munkitools- ./munkiextracted
  3. Rename the expanded munkitools packages.
    • mv ./munkiextracted/munkitools_admin- ./munkiextracted/munkitools_admin-
    • mv ./munkiextracted/munkitools_app-4.3.3035.pkg ./munkiextracted/munkitools_app-4.3.3035_extracted.pkg
    • mv ./munkiextracted/munkitools_core- ./munkiextracted/munkitools_core-
    • mv ./munkiextracted/munkitools_launchd- ./munkiextracted/munkitools_launchd-
  4. Reflatten each munkitools package
    • /usr/sbin/pkgutil --flatten ./munkiextracted/munkitools_admin- ./munkiextracted/munkitools_admin-
    • /usr/sbin/pkgutil --flatten ./munkiextracted/munkitools_app-4.3.3035_extracted.pkg ./munkiextracted/munkitools_app-4.3.3035.pkg
    • /usr/sbin/pkgutil --flatten ./munkiextracted/munkitools_core- ./munkiextracted/munkitools_core-
    • /usr/sbin/pkgutil --flatten ./munkiextracted/munkitools_launchd- ./munkiextracted/munkitools_launchd-
  5. Delete the old extracted packages
    • rm -rf /munkiextracted/munkitools_admin-
    • rm -rf ./munkiextracted/munkitools_app-4.3.3035_extracted.pkg
    • rm -rf ./munkiextracted/munkitools_core-
    • rm -rf ./munkiextracted/munkitools_launchd-

Creating a postinstall pkg with munkipkg

  1. Inside of the working directory, clone the munkipkg repository via git.
    • git clone https://github.com/munki/munki-pkg.git
  2. Create a new munkipkg sub directory and scripts directory
    • mkdir -p ./munki-pkg/munki_config/scripts
  3. Copy the build-info.json from munki_kickstart
    • cp ./munki-pkg/munki_kickstart/build-info.json ./munki-pkg/munki_config
  4. Using your text editor of choice (Atom, TextWrangler, vi, nano) configure the project’s identifier, name, but most importantly, change distribution_style from true to false.
     "ownership": "recommended",
     "suppress_bundle_relocation": true,
     "identifier": "com.github.munki.pkg.munki_config",
     "postinstall_action": "none",
     "distribution_style": false,
     "version": "1.0",
     "name": "munki_config.pkg",
     "install_location": "/"
    • You cannot have nested distribution packages!
  5. Create your postinstall script. Save this file as postinstall in ./munki-pkg/munki_config/scripts . This example will configure the SoftwareRepoURL for munki. Adapt as needed.

if [ "$3" == "/" ]; then

if [ "$3" == "/" ]; then
    defaults write /Library/Preferences/ManagedInstalls SoftwareRepoURL -string "https://yourmunkirepo.tld"

Now simply create your munki_config package using the munkipkg binary. ./munki-pkg/munkipkg ./munki-pkg/munki_config

If done correctly, you should see something like this:


Creating a new, signed distribution package - Part 1

  1. Move your newly created configuration package to the location of the other extracted packages
    • cp ./munki-pkg/munki_config/build/munki_config.pkg ./munkiextracted
    • If done correctly, you should see something like this in your munkiextracted folder: munki_custom_2.png
  2. Using productbuild, specify the paths to your packages and create a new distribution file
    productbuild --synthesize \
    --package ./munkiextracted/munkitools_admin- \
    --package ./munkiextracted/munkitools_app-4.3.3035.pkg \
    --package ./munkiextracted/munkitools_core- \
    --package ./munkiextracted/munkitools_launchd- \
    --package ./munkiextracted/munki_config.pkg \
    • Pay close attention here, as your packages will be installed in the order that you specify.
  3. Using your text editor of choice (Atom, TextWrangler, vi, nano), open both Distribution and Distribution_custom.plist

  4. On line 8 of Distribution_custom.plist, replace it with the following from the Distribution file and save.
<options customize="allow" allow-external-scripts="yes"/>
<domains enable_anywhere="true"/>
<installation-check script="requirerestart()"/>
function requirerestart() {
if (!(system.run('launchctl.py') == 1)) {
// == true is equal to return code 0. Therefore if your script returns 0 (aka true)
// return true to select all of the options or false to deselect.
return "None";
return "RequireRestart";
  • You can find an example of the entire distribution_custom.plist here

Still with me?

I hope so. Let’s quickly recap what we are doing here.

launchctl.py is a python script I wrote to detect in what context munki is being installed. Starting with macOS 10.7 and higher, you can have an external script perform an installation-check. Since munki will be downloaded/installed prior to the user session, we want to ensure the package doesn’t require a reboot. So long as the package is installed prior to the user session, this will work perfectly.

The distribution plist is the “brains” behind the package itself. It tells the package what to install and in what order. Remember, when creating a custom package, the order matters. If you are attempting to do something clever, but your package order is incorrect, it won’t work.

Also, the new munkitools launchd package has a postinstall that will also detect if it’s being installed prior to the user session and will load the daemons.

Something to point out here, depending on your MDM, the package could install after the user session. You may want to test this and see how the package behaves. Think of both the postinstall and launchctl.py as a “best effort”.

Back to it…

Creating a new, signed distribution package - Part 2

  1. Go to the ./munkiextracted directory
    • cd ./munkiextracted
  2. Create your new package with productbuild
    /usr/bin/productbuild \
    --distribution ./Distribution_custom.plist \
    --resources . \
    --scripts ./Scripts \
    --sign "Developer ID Installer: Example (R9UM25C6B5)" \
    • If successful, terminal will output productbuild: Wrote product to ./custom_munki.pkg

You can check the contents of your new package with Suspicious Package. If done correctly, you will see launchtl.py, pre/postinstalls for the standard munkipkgs and your custom pkg with a postinstall.


That’s it!

And with that, you are ready to go forth and deploy either a standard munki configuration or a custom package tailored to your company.

Table Of Contents