Harden macOS for engineers with FileVault, Gatekeeper, system extensions
For engineers locking down managed or self-managed Macs without breaking dev workflows. You will enable and verify FileVault, enforce Gatekeeper defaults, pre-approve system/network extensions with MDM, and confirm the machine is in a supportable state for EDR and corporate access.
TL;DR — The fastest path to a supportable macOS baseline is: turn on FileVault, leave Gatekeeper enabled at the default assessment level, and use MDM configuration profiles to pre-approve the exact system extensions, network extensions, and login items your EDR/VPN/dev tools need. The most common failure is trying to click through extension prompts locally instead of shipping the approvals by profile first. Reading time: ~5 min
Goal
When you are done, the Mac has FileVault enabled, Gatekeeper assessments active, required system or network extensions approved by profile instead of ad-hoc user prompts, and MDM profiles installed and visible in system state so EDR/VPN/dev tooling can run without repeated approval dialogs.
Prerequisites
- Admin access on the Mac, or a local account that can run
sudo -v - If the Mac is managed: access to your MDM to upload/install configuration profiles
- macOS 13+ recommended; check with:
sw_vers
- Xcode Command Line Tools for
profiles,spctl,systemextensionsctl; check with:
xcode-select -p
- Your org's Team IDs and bundle identifiers for each approved tool (EDR, VPN, USB filter, virtualization helper, etc.)
- If FileVault recovery is escrowed: the MDM must already support FileVault key escrow for macOS
- A maintenance window if you are changing FileVault state on a laptop that cannot be interrupted
Steps
Step 1: Check the current security state
Run these commands first so you know what you are changing.
fdesetup status
spctl --status
systemextensionsctl list
profiles status -type enrollment
profiles show -type configuration
You should see FileVault state, assessments enabled, a list of active/waiting extensions, and either an MDM enrollment status or Enrolled via DEP: Yes/No depending on the Mac.
Typical output shape:
$ fdesetup status
FileVault is Off.
$ spctl --status
assessments enabled
$ systemextensionsctl list
1 extension(s)
--- com.apple.system_extension.network_extension
enabled active teamID bundleID (version) name [state]
* * ABCD123456 com.vendor.agent.network-extension (3.2.1/45) Vendor Agent [activated enabled]
Step 2: Turn on FileVault
⚠️ Enabling FileVault starts full-disk encryption. On portable Macs, keep power connected and do not force shutdown during the initial encryption pass.
If the Mac is MDM-managed and your org escrows recovery keys, trigger FileVault from MDM first. If you need to enable it locally, use:
sudo fdesetup enable -user "$USER"
If you need to add another unlock user after FileVault is on:
sudo fdesetup add -usertoadd alice
Check progress:
fdesetup status
You should see either FileVault is On. or Encryption in progress: Percent completed = ....
Step 3: Confirm Gatekeeper is enabled and reset local overrides
Do not disable Gatekeeper globally for developer convenience; notarization and quarantine checks catch real malware. Reset the machine to the default assessment state:
sudo spctl --master-enable
spctl --status
Clear any broad local labels/rules you previously added for testing:
sudo spctl --remove --label "Developer ID"
sudo spctl --remove --label "Local Exceptions"
If you need to inspect why an app is blocked, assess the exact app bundle:
spctl --assess --type execute --verbose=4 "/Applications/SomeApp.app"
Success looks like accepted for trusted apps and assessments enabled for the global state.
Example blocked output shape:
$ spctl --assess --type execute --verbose=4 "/Applications/OldTool.app"
/Applications/OldTool.app: rejected
source=no usable signature
Step 4: Pre-approve system extensions and network extensions with MDM
Install a System Extensions payload from your MDM using the literal Team ID and bundle IDs for your tools. If your MDM accepts raw mobileconfig, use a profile like this and replace the placeholders.
<?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">
<dict>
<key>PayloadContent</key>
<array>
<dict>
<key>PayloadType</key>
<string>com.apple.system-extension-policy</string>
<key>PayloadVersion</key>
<integer>1</integer>
<key>PayloadIdentifier</key>
<string>com.example.security.sysex</string>
<key>PayloadUUID</key>
<string>11111111-2222-3333-4444-555555555555</string>
<key>PayloadDisplayName</key>
<string>Approved System Extensions</string>
<key>AllowedTeamIdentifiers</key>
<array>
<string>ABCD123456</string>
</array>
<key>AllowedSystemExtensions</key>
<dict>
<key>ABCD123456</key>
<array>
<string>com.vendor.agent.network-extension</string>
<string>com.vendor.agent.endpoint-extension</string>
</array>
</dict>
</dict>
</array>
<key>PayloadType</key>
<string>Configuration</string>
<key>PayloadVersion</key>
<integer>1</integer>
<key>PayloadIdentifier</key>
<string>com.example.security</string>
<key>PayloadUUID</key>
<string>aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee</string>
<key>PayloadDisplayName</key>
<string>Security Baseline</string>
</dict>
</plist>
After the profile lands, inspect extension state:
systemextensionsctl list
You should see the extension move from waiting/not activated to activated enabled after the vendor app is launched or its agent is loaded.
Step 5: Approve login/background items with MDM if your tool uses them
Many EDR, VPN, sync, and helper apps now expose managed login/background items. In your MDM, install a Service Management payload for the exact bundle identifiers your vendor documents. Then verify locally:
sfltool dumpbtm
Look for your app's bundle ID in the background task management dump. Success is the item appearing as managed/allowed rather than prompting each user.
Step 6: Verify the MDM profile is actually installed and not just uploaded
A common mistake is uploading a profile to MDM but not scoping it to the device group. Check local profile presence:
profiles show -type configuration | egrep -A5 "system-extension-policy|PayloadDisplayName|PayloadIdentifier"
If the Mac is supervised/enrolled, also check:
profiles status -type enrollment
You should see the profile identifier/display name you installed and an enrolled status appropriate to your fleet.
Verify it works
Run the full verification set:
fdesetup status
spctl --status
systemextensionsctl list
sfltool dumpbtm | egrep "managed|bundleID|identifier"
profiles show -type configuration | egrep -A5 "system-extension-policy|PayloadDisplayName|PayloadIdentifier"
Expected results:
fdesetup statusreturnsFileVault is On.or an in-progress percentagespctl --statusreturns:
assessments enabled
systemextensionsctl listshows required extensions asactivated enabledprofiles show -type configurationincludes your security baseline profile and the system extension policy payload- Your EDR/VPN/dev tool launches without a new "System Extension Blocked" or background item approval prompt
Useful flow when diagnosing approval failures:
View as text
[MDM] ---- 1. install profile ----> [macOS]
| |
| 2. sysex policy present | 3. app launches
v v
[profiles show] [systemextensionsctl]
|
| 4. activated enabled
v
[EDR/VPN app]Common pitfalls
FileVault enabled locally before recovery-key escrow exists
Mistake: running fdesetup enable on a managed Mac before the escrow profile is installed.
Symptom: FileVault turns on, but your org has no recovery key in MDM and support asks you to rotate it later.
Fix: install the FileVault escrow profile first, then rotate the key using your MDM workflow or vendor-documented fdesetup changerecovery -personal process.
Disabling Gatekeeper to run one unsigned tool
Mistake: using sudo spctl --master-disable and forgetting to turn it back on.
Symptom: spctl --status returns:
assessments disabled
Fix: run:
sudo spctl --master-enable
Wrong Team ID or bundle ID in the system extension profile
Mistake: approving the app bundle ID instead of the extension bundle ID, or copying the wrong Team ID from a web page.
Symptom: the app still shows "System Extension Blocked" and systemextensionsctl list shows it waiting or absent.
Fix: get the exact Team ID and extension bundle ID from the vendor package/signing info, update the profile, and redeploy it.
Uploading the profile to MDM but not assigning it to the Mac
Mistake: profile exists in the MDM console but is not scoped to the device or smart group.
Symptom: profiles show -type configuration does not list your payload at all.
Fix: assign the profile to the target device group, force a device sync from MDM, then rerun profiles show -type configuration.
Expecting local clicks to survive policy changes
Mistake: manually allowing an extension in System Settings on one machine and assuming that is your rollout plan.
Symptom: new Macs or rebuilt Macs prompt again; some headless agents never activate because no user is present to click Allow.
Fix: ship the approval via MDM profile, not by local UI approval.
Confusing app notarization failures with extension approval failures
Mistake: treating a blocked app launch and a blocked system extension as the same problem.
Symptom: spctl --assess says rejected even though your system extension profile is correct.
Fix: solve the app trust issue first by using a signed/notarized build or removing quarantine only for that artifact if your org permits it; then handle extension approval separately.
This article was written by an AI system and published pending human review. Verify anything you intend to act on.
Have a project in mind?
Get an instant AI price estimate for it, or talk directly to our team.
One email a month on what we learn building with AI