A walkthrough record of remediating 26 failed CIS Benchmark checks on a Mac mini (Apple Silicon, M2) running macOS 26.x.
A CIS Benchmark scan (cis_macOS_26.x.yml) reported 26 failed checks. This document records what each check actually meant, which were genuine issues, which were already in compliance, and the commands used to remediate.
Several "failed" checks were already in compliance on this machine. Verified before remediation:
| Check | Scan reported | Actual state |
|---|---|---|
| #2 Firewall | failed | Already enabled (State = 1) |
| #3 Firewall Stealth Mode | failed | Already on |
| #4 FileVault | failed | Already On (fdesetup enable returned "already On") |
These appear to be false negatives — most likely because the scan checks for a managed configuration profile (MDM-deployed) rather than the actual system state. On a non-managed personal machine, the underlying setting can be correctly configured but the scan flags it because no profile is present.
Lesson: Always verify the actual system state before assuming a "failed" check needs action.
Status legend:
- ✅ Applied & verified
- ☑️ Already in compliance (false positive)
⚠️ Command provided, verification pending- ⏭️ Skipped (not applicable or benign)
| # | Check | Status |
|---|---|---|
| 1 | Install Application Updates from App Store | |
| 2 | Firewall enabled | ☑️ |
| 3 | Firewall stealth mode | ☑️ |
| 4 | FileVault enabled | ☑️ |
| 5 | Disable Help Apple Improve Search | |
| 6 | Disable Power Nap (Intel only) | ⏭️ N/A on Apple Silicon |
| 7 | Custom login screen message | ✅ |
| 8 | Login window: Name and Password | |
| 9 | Disable password hints | |
| 10 | On-Device Dictation | ⏭️ Dictation never enabled (benign) |
| 11 | Security auditing (auditd) | ⏭️ Subsystem deprecated in macOS 26 |
| 12 | install.log retention 365+ days | ✅ |
| 13 | Disable Bonjour advertising | ✅ (effective on reboot) |
| 14 | Disable NFS server | |
| 15 | Password lockout threshold | ✅ |
| 16 | Password minimum length (15) | ✅ |
| 17 | Password must contain alphabetic | ✅ |
| 18 | Password must contain numeric | ✅ |
| 19 | Password must contain special | ✅ |
| 20 | Password must contain mixed case | ✅ |
| 21 | Password max age (365 days) | ✅ |
| 22 | Password history (24) | ✅ |
| 23 | Sudo timeout = 0 | ✅ |
| 24 | Root account disabled | Partial — disabled, but shell fix |
| 25 | Login window banner | ✅ |
| 26 | Sudo logging enabled | ✅ |
Auto-install of App Store app updates. Patches need to land without manual intervention to reduce vulnerability window.
sudo defaults write /Library/Preferences/com.apple.commerce AutoUpdate -bool TRUERequires logout/login for GUI to reflect.
Verified with:
/usr/libexec/ApplicationFirewall/socketfilterfw --getglobalstate
# Firewall is enabled. (State = 1)No action needed. Scan false positive.
Verified with:
/usr/libexec/ApplicationFirewall/socketfilterfw --getstealthmode
# Firewall stealth mode is onNo action needed. Scan false positive.
Verified with:
sudo fdesetup status
# FileVault is On.No action needed. Scan false positive.
Privacy control. Stops Spotlight/Siri search queries from being sent to Apple.
defaults write com.apple.assistant.support "Search Queries Data Sharing Status" -int 2Or GUI: System Settings → Spotlight → toggle off Help Apple Improve Search.
Power Nap is an Intel-only feature. This M2 Mac mini doesn't have it. False positive — ignore.
sudo defaults write /Library/Preferences/com.apple.loginwindow LoginwindowText "If found, please contact nat@re.je"To remove later: sudo defaults delete /Library/Preferences/com.apple.loginwindow LoginwindowText
Hide the user list at login; require typing both username and password.
sudo defaults write /Library/Preferences/com.apple.loginwindow SHOWFULLNAME -bool trueUsername on this machine is nat (short name) or Nathaniel Hey (full name) — either authenticates.
sudo defaults write /Library/Preferences/com.apple.loginwindow RetriesUntilHint -int 0Setting 0 means hint is never displayed regardless of failed attempts.
Dictation was never enabled on this machine (preference key didn't exist). No voice data is leaving the device, so the underlying risk is moot. CIS check fails on a technicality but no action required.
Apple has effectively retired the BSM audit subsystem in macOS 26. The /var/audit/ directory doesn't exist; auditd loads but has nowhere to write. Security event logging continues via the unified log instead:
log show --predicate 'eventMessage contains "authentication"' --last 5mCIS benchmark hasn't kept pace with macOS evolution. Accept as benchmark-vs-reality mismatch.
Modified /etc/asl/com.apple.install to retain logs ≥365 days with no size cap.
# Backup first
sudo cp /etc/asl/com.apple.install /etc/asl/com.apple.install.bak
# Apply new config
sudo sed -i '' 's|.*file /var/log/install.log.*|* file /var/log/install.log format='"'"'$((Time)(JZ)) $Host $(Sender)[$(PID)]: $Message'"'"' rotate=utc compress ttl=365|' /etc/asl/com.apple.installResulting line:
* file /var/log/install.log format='$((Time)(JZ)) $Host $(Sender)[$(PID)]: $Message' rotate=utc compress ttl=365
Changes: added ttl=365, removed file_max=50M and all_max=150M, changed rotate=seq to rotate=utc.
Stops the Mac from broadcasting itself + service info to the local network. Tradeoff: breaks AirDrop visibility, AirPlay receive, and similar discovery-based features.
sudo defaults write /Library/Preferences/com.apple.mDNSResponder.plist NoMulticastAdvertisements -bool truelaunchctl kickstart is blocked by SIP — change takes effect on next reboot.
To revert:
sudo defaults delete /Library/Preferences/com.apple.mDNSResponder.plist NoMulticastAdvertisementssudo nfsd disable
# Optional cleanup
sudo rm -f /etc/exportsBecause pwpolicy setaccountpolicies is a replace operation (not merge), all eight password-related controls were applied as a single combined policy.
Settings applied (all CIS defaults):
| # | Control | Value |
|---|---|---|
| 15 | Lockout threshold | 5 attempts, 900s (15 min) lockout |
| 16 | Minimum length | 15 characters |
| 17 | Must contain alphabetic | Required |
| 18 | Must contain numeric | Required |
| 19 | Must contain special | Required |
| 20 | Must contain mixed case | Required |
| 21 | Maximum age | 365 days |
| 22 | History | Cannot reuse last 24 |
Backup current policy first:
sudo pwpolicy -getaccountpolicies > ~/pwpolicy_backup_$(date +%Y%m%d).plistApply policy:
cat <<'EOF' | sudo pwpolicy setaccountpolicies /dev/stdin
<?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>policyCategoryAuthentication</key>
<array>
<dict>
<key>policyContent</key>
<string>(policyAttributeFailedAuthentications < policyAttributeMaximumFailedAuthentications) OR (policyAttributeCurrentTime > (policyAttributeLastFailedAuthenticationTime + autoEnableInSeconds))</string>
<key>policyIdentifier</key>
<string>Authentication Lockout</string>
<key>policyParameters</key>
<dict>
<key>autoEnableInSeconds</key>
<integer>900</integer>
<key>policyAttributeMaximumFailedAuthentications</key>
<integer>5</integer>
</dict>
</dict>
</array>
<key>policyCategoryPasswordContent</key>
<array>
<dict>
<key>policyContent</key>
<string>policyAttributePassword matches '.{15,}+'</string>
<key>policyIdentifier</key>
<string>Password Minimum Length 15</string>
</dict>
<dict>
<key>policyContent</key>
<string>policyAttributePassword matches '(.*[a-zA-Z].*)+'</string>
<key>policyIdentifier</key>
<string>Must Contain Alphabetic</string>
</dict>
<dict>
<key>policyContent</key>
<string>policyAttributePassword matches '(.*[0-9].*)+'</string>
<key>policyIdentifier</key>
<string>Must Contain Numeric</string>
</dict>
<dict>
<key>policyContent</key>
<string>policyAttributePassword matches '(.*[^a-zA-Z0-9].*)+'</string>
<key>policyIdentifier</key>
<string>Must Contain Special</string>
</dict>
<dict>
<key>policyContent</key>
<string>policyAttributePassword matches '(.*[A-Z].*)+' AND policyAttributePassword matches '(.*[a-z].*)+'</string>
<key>policyIdentifier</key>
<string>Must Contain Mixed Case</string>
</dict>
</array>
<key>policyCategoryPasswordChange</key>
<array>
<dict>
<key>policyContent</key>
<string>policyAttributeCurrentTime < (policyAttributeLastPasswordChangeTime + (policyAttributeExpiresEveryNDays * 24 * 60 * 60))</string>
<key>policyIdentifier</key>
<string>Password Max Age 365</string>
<key>policyParameters</key>
<dict>
<key>policyAttributeExpiresEveryNDays</key>
<integer>365</integer>
</dict>
</dict>
<dict>
<key>policyContent</key>
<string>none policyAttributePasswordHashes in policyAttributePasswordHistory</string>
<key>policyIdentifier</key>
<string>Password History 24</string>
<key>policyParameters</key>
<dict>
<key>policyAttributePasswordHistoryDepth</key>
<integer>24</integer>
</dict>
</dict>
</array>
</dict>
</plist>
EOFVerify:
sudo pwpolicy -getaccountpoliciesNotes:
- Existing passwords are not affected — rules apply on next change.
- 365-day max age means macOS will prompt for change in ~1 year.
Rollback:
sudo pwpolicy -clearaccountpolicies
# Or restore backup:
sudo pwpolicy setaccountpolicies ~/pwpolicy_backup_*.plistEvery sudo command now requires a fresh password — no 5-minute caching window.
echo "Defaults timestamp_timeout=0" | sudo tee /etc/sudoers.d/10_cissudoconfiguration
sudo chmod 440 /etc/sudoers.d/10_cissudoconfiguration
sudo chown root:wheel /etc/sudoers.d/10_cissudoconfigurationFilename note: macOS ignores files in /etc/sudoers.d/ containing . — do not add an extension.
To revert: sudo rm /etc/sudoers.d/10_cissudoconfiguration
Root was already disabled (AuthenticationAuthority key absent) but shell was /bin/sh. Set to /usr/bin/false so even non-interactive invocations of root produce no shell:
sudo dscl . -create /Users/root UserShell /usr/bin/falseVerify:
dscl . -read /Users/root UserShell
# UserShell: /usr/bin/falsePre-login text banner displayed before authentication. Distinct from the lock screen message in #7.
sudo tee /Library/Security/PolicyBanner.txt > /dev/null <<'EOF'
This system is for the use of authorized users only. Use of this system constitutes consent to monitoring. Unauthorized access is prohibited.
EOF
sudo chmod 644 /Library/Security/PolicyBanner.txtTo remove: sudo rm /Library/Security/PolicyBanner.txt
Appends Defaults log_allowed to the sudoers config from #23. Privileged commands now logged to the unified log.
echo "Defaults log_allowed" | sudo tee -a /etc/sudoers.d/10_cissudoconfigurationFinal contents of /etc/sudoers.d/10_cissudoconfiguration:
Defaults timestamp_timeout=0
Defaults log_allowed
View sudo log entries:
log show --predicate 'process == "sudo"' --last 10mAfter reboot, re-run the CIS scan. Expected remaining failures:
- #6 (Power Nap) — N/A on Apple Silicon
- #10 (Dictation) — only flags if dictation is enabled in on-device mode; safe to ignore if dictation is off
- #11 (Security auditing) — deprecated subsystem
Items #1, #5, #8, #9, #14, #24 — verify these were applied since they were not directly confirmed during the walkthrough:
# #1
defaults read /Library/Preferences/com.apple.commerce AutoUpdate
# #5
defaults read com.apple.assistant.support "Search Queries Data Sharing Status"
# #8
defaults read /Library/Preferences/com.apple.loginwindow SHOWFULLNAME
# #9
defaults read /Library/Preferences/com.apple.loginwindow RetriesUntilHint
# #14
sudo nfsd status
# #24
dscl . -read /Users/root UserShell| Path | Purpose |
|---|---|
/etc/asl/com.apple.install |
install.log retention config (with .bak backup) |
/etc/sudoers.d/10_cissudoconfiguration |
Sudo timeout + logging |
/Library/Security/PolicyBanner.txt |
Login window banner |
~/pwpolicy_backup_YYYYMMDD.plist |
Pre-change password policy backup |
# Password policy
sudo pwpolicy -clearaccountpolicies
# Sudo timeout + logging
sudo rm /etc/sudoers.d/10_cissudoconfiguration
# install.log retention
sudo cp /etc/asl/com.apple.install.bak /etc/asl/com.apple.install
# Bonjour advertising
sudo defaults delete /Library/Preferences/com.apple.mDNSResponder.plist NoMulticastAdvertisements
# Login banner
sudo rm /Library/Security/PolicyBanner.txt
# Login screen message
sudo defaults delete /Library/Preferences/com.apple.loginwindow LoginwindowText
# Login window: revert to user list
sudo defaults delete /Library/Preferences/com.apple.loginwindow SHOWFULLNAME
# Password hints
sudo defaults delete /Library/Preferences/com.apple.loginwindow RetriesUntilHint