Munki Repo Security: Four Locks Between Your plists and Every Mac You Own

Hero image for Munki Repo Security: Four Locks Between Your plists and Every Mac You Own
PC Drama
7 views

Part of the Munki and plist Files: How One Folder of Text Serves Software for Mac Clients. Start there for the wide view, then come back here for the close-up.

Munki repo security comes down to four locks, and the project ships with only one of them turned. A Munki repository is a folder of plain text plists on a web server, which means every Mac in your fleet takes its installation orders from files anyone with the URL can read and anyone with the share can rewrite. Each lock is a documented setting or a server directive, not a product you have to buy. Here is the map.

Lock What it stops Where it lives Default
The wire Reading or rewriting manifests in transit HTTPS on the server, SoftwareRepoCACertificate, FollowHTTPRedirects Plain HTTP works; redirects are not followed
The client Any device with the URL pulling licensed software AdditionalHttpHeaders (basic auth) or UseClientCertificate Open to anyone who can reach the server
The payload A swapped or corrupted installer installer_item_hash plus PackageVerificationMode hash: checked only when a hash exists
The pen Anyone rewriting a manifest or catalog File share permissions, Git over SSH, repo plugins Whatever your file server allows

The Munki Trust Map

Four translucent glass layers stacked and offset above a dark grid, the top layer edged in cyan light
Four sheets, four locks. Each one guards a different question, and none of them can answer the others.

The rows are not interchangeable. HTTPS keeps the wire honest but says nothing about who is on the other end of it. Client authentication decides who may read the repo but nothing about whether what they read was tampered with at rest. Package hashes prove the installer matches the pkginfo but the pkginfo itself carries no hash, so a rewritten catalog sails straight through. That gap is why the fourth lock is the only one Munki cannot enforce for you. It is a property of your file server, your Git host, or your repo plugin.

Munki's own documentation draws the line cleanly: HTTPS "only secures the contents of the data on the wire as it passes from host to host" and "does not have the facility to control which hosts or users can access a site."

Lock the Wire: HTTPS, the CA Cert, and Redirects

Serve the repo over TLS and point the client at a certificate authority it trusts. If you run an internal CA, Munki looks for the CA bundle at /Library/Managed Installs/certs/ca.pem by default, or wherever SoftwareRepoCACertificate says, with SoftwareRepoCAPath available for a directory of certificates. The SoftwareRepoURL itself should start with https://, and if you ever plan to use client certificates it must.

By default, Munki refuses to follow redirects. The FollowHTTPRedirects preference accepts none, https, or all, and the middle value is the sane one: it lets a load balancer bounce a client to a secure host while refusing any redirect that would downgrade the connection to plain HTTP. Setting it to all hands a stray redirect the power to move your fleet to a server you do not control.

Prove the Client: Basic Auth or Certificates

A brass key resting on a dark desk beside a closed laptop, with a blank smart card lying behind it
A shared password or a per-device certificate. Both open the door; only one knows who walked through.

Munki gives you two ways to make the server ask "who are you?" before it hands over a manifest.

HTTP basic auth SSL client certificates
Client setting AdditionalHttpHeaders array carrying Authorization: Basic and a base64 username:password UseClientCertificate true, PEM at /Library/Managed Installs/certs/
Server side An .htpasswd file and Require valid-user in the repo's directory block Your own CA, a secure virtual host, and SSLRequire rules matching the certificate's DN
Per-device identity Only if you issue one password per client Built in; UseClientCertificateCNAsClientIdentifier can even pick the manifest from the cert
Revocation Change the password, redeploy the header Revoke the certificate at the CA
Setup effort Minutes The wiki's own demo scripts come with a disclaimer

Basic auth is what the documentation itself calls "much easier to set up," and paired with HTTPS and a signed server certificate it rates as "reasonably secure." The catch is where the password lands. Written to /Library/Preferences/ManagedInstalls.plist, the header is readable by every user on the Mac, so any of them can lift the credential and browse the repo. The documented fix: write AdditionalHttpHeaders into root's preferences at /private/var/root/Library/Preferences/ManagedInstalls.plist, which non-admins cannot read, or deliver it in a configuration profile from your MDM. Either way, confirm the client sees it with sudo managedsoftwareupdate --show-config.

"Setting up an SSL-secured webserver (one that requires clients to have certificates to connect) is probably the way to go, but setting this up is tricky."

Greg Neagle creator of Munki, on the munki-dev list in October 2010, as archived at Google Groups

Certificates are the stronger lock because they identify the device rather than a shared secret, and Munki 7.3, released in August 2026, added ClientCertificateAcceptableCAs so the client can pick the right identity even behind load balancers that never advertise their CA list. But the wiki's client certificate walkthrough still carries the warning that its scripts are "only intended to exemplify" the setup, and its sample certificates are 1024 bits, which newer Apache builds reject. With an MDM that can install identities, place the PEM that way and skip the scripts. Without one, basic auth in root's preferences is the honest choice.

Verify the Payload: installer_item_hash and hash_strict

A stream of violet particles settling into a glowing rectangular lattice of cells, half filled and half still forming
A SHA-256 hash proves the download is the download. It says nothing about who wrote the pkginfo that named it.

When makepkginfo or munkiimport builds a pkginfo, it writes an installer_item_hash, the SHA-256 of the installer item. On the client, PackageVerificationMode decides what to do with it. The default, hash, verifies a download only when the pkginfo carries a hash. hash_strict verifies every download and fails any item that has no hash to check. none skips the check entirely, and the FAQ marks that one "not recommended."

The default is the polite setting: it never breaks an old pkginfo that predates hashing. It is also the setting that lets a hand-edited pkginfo with the hash key deleted install whatever now sits at that path. Turn on hash_strict, then re-import anything that fails, because a missing hash is a finding, not an inconvenience. The hash proves the installer matches the pkginfo. It does not prove the pkginfo, the catalog, or the manifest match what you wrote. That integrity rests on the wire lock and the pen lock, never on this one.

PackageVerificationMode, mode by mode
Value Behavior When an item has no installer_item_hash
none No integrity check at all Installs
hash (default) Checks the SHA-256 when one is present Installs
hash_strict Checks the SHA-256 on every download Fails with an integrity error

An "integrity check failed" message means the downloaded item's SHA-256 does not match the pkginfo. The FAQ's first suggestion is to re-import the item so the hash is regenerated, not to delete the key.

Guard the Pen: Who Writes to the Repo

Top-down view of a small network switch on a dark desk with three patch cables, one port lit green, and a brass key beside it
Clients read through the switch. Admins write with the key. Munki secures the first path and hands you the second.

Clients read over HTTPS. Admins write somewhere else entirely, and that somewhere is the lock Munki leaves to you. The admin tools reach the repo through a repo plugin. The default, FileRepo, handles a local folder or a file share reached by an afp://, smb://, or nfs:// URL, so the write boundary is simply whoever can mount that share. An https:// repo URL means a custom plugin such as MWA2APIRepo, where the API's own authentication becomes the boundary.

The project's Git guide is the cleanest answer to the pen question. Serve the repo over HTTP or HTTPS, keep write access to the Git repository on SSH with key authentication, and let every manifest change arrive as a commit with a name on it. The guide lists a benefit that reads like a security control because it is one: "a change in the repo is not immediately available to clients." A review step between the commit and the deploy is the difference between a typo and an incident. Its one blunt rule: "Version control is not an alternative for backups. And backing up is not an alternative for version control. Use both."

Serve From the Cloud Without Opening the Bucket

A repo in S3, Google Cloud Storage, or Azure Storage needs a signature the bucket will honor. That is what Munki middleware is for: a module that "can alter an HTTP(S) request to work with servers (often cloud-based) that require specific headers, keys, or encrypted/signed requests." Only a single module loads. In Munki 7 it is a .plugin dylib in /usr/local/munki/middleware. In the Python era it was a middleware*.py file in /usr/local/munki, and the documentation told you to chown root and chmod 600 it precisely because it holds credentials.

The CloudFront middleware shows the shape of a good one. It signs each request with a private key, every signature carries an expiry, and the default lifetime is 60 minutes. The key sits at /usr/local/munki/munkiaccess.pem owned by root with mode 400, and AWS allows two CloudFront keys per account so you can rotate without downtime. Since version 1.1 the certificate and settings can ride in a configuration profile, which turns key rotation into an MDM push. Treat that private key like the repo password: it is the client lock, wearing a cloud uniform.

Middleware modules ported to Munki 7
Module Backend What it signs or adds
CloudFrontMiddleware Amazon CloudFront in front of S3 Signed URLs with an expiry
S3Middleware Amazon S3 direct AWS request signatures
GCSMiddleware Google Cloud Storage Signed requests
AzureStorageMiddleware Azure Blob Storage Shared access signatures
BunnyNetMiddleware Bunny.net CDN Token authentication headers
DemoMiddleware None Prints the request; a template for your own

The Munki wiki notes these ports are "believed to work" but "not officially supported at this time," and asks the admins who run them to take over maintenance.

Where the Secrets Live on the Mac

Every lock above ends in a preference on the client, and Munki reads preferences from four places in a fixed order. A managed profile wins over root's ByHost plist, which wins over root's plist, which wins over the world-readable /Library/Preferences/ManagedInstalls.plist. Put the repo URL, the auth header, and the verification mode in a profile or in root's file, and leave the world-readable file for the settings Managed Software Center needs to read as the logged-in user. Edit them with defaults or a profile, never a text editor, because macOS caches preferences and may ignore the change.

Two older protections deserve a mention because they are on by default. Preflight and postflight scripts must be owned by the user running managedsoftwareupdate, share its group or wheel, and must not be world-writable, or Munki refuses to run them. And every official Munki 7 release is signed and notarized by Mac Admins Open Source, so the tool itself can be verified.

Where Munki reads its preferences, highest precedence first
Location Who can read it Best for
MCX or configuration profiles Managed by MDM Everything, including AdditionalHttpHeaders
/private/var/root/Library/Preferences/ByHost/ManagedInstalls.XXXXXX.plist Root and admins Rare per-host overrides
/private/var/root/Library/Preferences/ManagedInstalls.plist Root and admins Repo URL, auth header, verification mode
/Library/Preferences/ManagedInstalls.plist Every user on the Mac Settings the GUI must read, such as ManagedInstallDir, HelpURL, and the MSC keys

Preferences set in root's file silently override the same keys in the world-readable file, which the wiki flags as a common source of confusion when two admins look in different places.

Frequently Asked Questions

Frequently Asked Questions
Does Munki need HTTPS?
It works over plain HTTP, but the project treats HTTPS as the baseline for any repo that holds licensed software, and both authentication methods assume it. Basic auth over unencrypted HTTP is described as trivially sniffable, and client certificates require an https:// repo URL outright.
Is basic auth or a client certificate more secure for Munki?
Client certificates identify each device and can be revoked individually, so they are the stronger lock. Basic auth is a shared secret that is much easier to deploy and, with HTTPS and the header stored in root's preferences or a profile, is documented as reasonably secure.
What does hash_strict change?
The default hash mode verifies a download only when the pkginfo has an installer_item_hash. hash_strict verifies every download and fails any item without a hash, so a pkginfo that lost its hash cannot install.
Does Munki verify manifests and catalogs?
No. The integrity hash covers installer items only. Manifests, catalogs, and pkginfo files are protected by TLS in transit and by whatever controls write access to the repo at rest.
Can I keep the repo password out of the world-readable plist?
Yes. Write AdditionalHttpHeaders to root's preferences at /private/var/root/Library/Preferences/ManagedInstalls.plist, or deliver it in a configuration profile. Both take precedence over the /Library copy.

About Munki Repo Security

Munki is an open source project created by Greg Neagle and maintained on GitHub, with signed and notarized releases published through Mac Admins Open Source.

Four locks, three of them a preference key away and the fourth a decision about who holds the pen. Turn them and the plain text folder that makes Munki so easy to read stays readable by you alone. For the wide-angle view of how those pkginfo, catalog, and manifest files fit together in the first place, start with our guide to Munki and plist files. And if the web server under your repo could use a second set of eyes, our cybersecurity team hardens servers for a living.

Sources: Munki wiki, Using Basic Authentication, Munki wiki, Using Munki With SSL Client Certificates, Munki wiki, Preferences, Munki wiki, Supported Pkginfo Keys, Munki wiki, Middleware, Munki wiki, Repo Plugins, Munki wiki, Munki With Git, Munki wiki, FAQ, Munki wiki, Release Notes, Munki releases on GitHub, CloudFront Middleware README, munki-dev, How to protect munki repo (October 2010)

Related Articles