198 lines
5.2 KiB
Markdown
198 lines
5.2 KiB
Markdown
# Server Hardening
|
|
|
|
Template for Part 2 of the setup script. All sudo commands.
|
|
|
|
## FileVault (MUST DISABLE)
|
|
|
|
FileVault disk encryption blocks unattended boot — the machine sits
|
|
at a pre-boot unlock screen waiting for a password before macOS even
|
|
loads. This is incompatible with headless server operation.
|
|
|
|
```bash
|
|
# Check status
|
|
fdesetup status
|
|
|
|
# Disable non-interactively (for MCP/scripted use)
|
|
printf '<?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>Username</key><string>USERNAME</string>
|
|
<key>Password</key><string>PASSWORD</string>
|
|
</dict></plist>' | sudo fdesetup disable -inputplist
|
|
|
|
# Monitor decryption progress (runs in background, machine stays usable)
|
|
fdesetup status
|
|
```
|
|
|
|
## Auto-Login
|
|
|
|
Required for headless operation — ensures the GUI session starts on
|
|
boot so user-level LaunchAgents (Tailscale, Signet, etc.) can run.
|
|
Cannot work while FileVault is enabled.
|
|
|
|
```bash
|
|
# Set auto-login user
|
|
sudo defaults write /Library/Preferences/com.apple.loginwindow autoLoginUser "USERNAME"
|
|
|
|
# Create kcpassword (XOR-obfuscated password file)
|
|
python3 -c "
|
|
password = 'PASSWORD'
|
|
key = [0x7d, 0x89, 0x52, 0x23, 0xd2, 0xbc, 0xdd, 0xea, 0xa3, 0xb9, 0x1f]
|
|
encoded = bytearray()
|
|
for i, c in enumerate(password + chr(0)):
|
|
encoded.append(ord(c) ^ key[i % len(key)])
|
|
while len(encoded) % 12 != 0:
|
|
encoded.append(0)
|
|
with open('/etc/kcpassword', 'wb') as f:
|
|
f.write(encoded)
|
|
"
|
|
sudo chmod 600 /etc/kcpassword
|
|
```
|
|
|
|
## Power Management
|
|
|
|
```bash
|
|
sudo pmset -a displaysleep 0 disksleep 0 sleep 0 \
|
|
powernap 0 autorestart 1 networkoversleep 1
|
|
```
|
|
|
|
## Application Firewall
|
|
|
|
```bash
|
|
FW=/usr/libexec/ApplicationFirewall/socketfilterfw
|
|
sudo $FW --setglobalstate on
|
|
sudo $FW --setallowsigned on
|
|
sudo $FW --setstealthmode on
|
|
```
|
|
|
|
## SMB Guest Access
|
|
|
|
**WARNING**: Never hardcode share names. macOS uses Unicode curly
|
|
quotes (`'` U+2019) in default names like "Mac's Public Folder",
|
|
which silently breaks `sharing -r` with straight quotes. Always
|
|
parse dynamically:
|
|
|
|
```bash
|
|
# Remove ALL share points dynamically
|
|
sharing -l | grep "^name:" | sed 's/name:[[:space:]]*//' | \
|
|
while read -r name; do
|
|
sudo sharing -r "$name" 2>/dev/null && \
|
|
echo "removed: $name" || echo "skip: $name"
|
|
done
|
|
|
|
SMBPREF=/Library/Preferences/SystemConfiguration/com.apple.smb.server
|
|
sudo defaults write $SMBPREF AllowGuestAccess -bool false
|
|
```
|
|
|
|
## Consumer Launch Agents
|
|
|
|
Disable via `launchctl disable gui/$UID/<label>`. This persists
|
|
across reboots without modifying plists (SIP-safe).
|
|
|
|
Services to disable:
|
|
|
|
```
|
|
com.apple.Siri.agent
|
|
com.apple.siriactionsd
|
|
com.apple.siriknowledged
|
|
com.apple.siriinferenced
|
|
com.apple.sirittsd
|
|
com.apple.siri-distributed-evaluation
|
|
com.apple.cloudphotod
|
|
com.apple.CloudPhotosConfiguration
|
|
com.apple.photolibraryd
|
|
com.apple.gamed
|
|
com.apple.GameController.gamecontrolleragentd
|
|
com.apple.GamePolicyAgent
|
|
com.apple.newsd
|
|
com.apple.weatherd
|
|
com.apple.tipsd
|
|
com.apple.Maps.mapssyncd
|
|
com.apple.findmymacmessenger
|
|
com.apple.icloud.findmydeviced.findmydevice-user-agent
|
|
com.apple.homed
|
|
com.apple.homeenergyd
|
|
com.apple.itunescloudd
|
|
```
|
|
|
|
**NOT disabled** (keep these): Safari agents, Zoom, Google/Chrome.
|
|
|
|
Pattern:
|
|
|
|
```bash
|
|
UID_NUM=$(id -u)
|
|
for svc in <list>; do
|
|
launchctl disable "gui/$UID_NUM/$svc" 2>/dev/null && \
|
|
echo " disabled: $svc" || echo " skip: $svc"
|
|
done
|
|
```
|
|
|
|
## Hostname
|
|
|
|
```bash
|
|
sudo scutil --set ComputerName "<name>"
|
|
sudo scutil --set HostName "<name>"
|
|
sudo scutil --set LocalHostName "<name>"
|
|
```
|
|
|
|
## Spotlight
|
|
|
|
```bash
|
|
sudo mdutil -a -i off
|
|
```
|
|
|
|
## Screen Sharing (VNC)
|
|
|
|
Use ARD kickstart — `launchctl load/bootstrap` for screensharing.plist
|
|
fails with I/O errors on modern macOS. The kickstart binary is the
|
|
official way.
|
|
|
|
Important: must use `-allowAccessFor -allUsers` and set VNC legacy
|
|
mode with an explicit password. Using `-specifiedUsers` can result in
|
|
broken naprivs values that cause auth failures.
|
|
|
|
```bash
|
|
KICKSTART=/System/Library/CoreServices/RemoteManagement/ARDAgent.app/Contents/Resources/kickstart
|
|
|
|
# Clean start — deactivate first if re-running
|
|
sudo $KICKSTART -deactivate -stop
|
|
|
|
# Activate with VNC legacy password (for non-macOS clients)
|
|
sudo $KICKSTART \
|
|
-activate -configure \
|
|
-allowAccessFor -allUsers \
|
|
-privs -all \
|
|
-clientopts -setvnclegacy -vnclegacy yes \
|
|
-setvncpw -vncpw <PASSWORD> \
|
|
-restart -agent
|
|
```
|
|
|
|
Connect via `vnc://<tailscale-ip>` or any VNC client on port 5900.
|
|
Over Tailscale, no extra firewall rules needed.
|
|
|
|
## Visual Effects
|
|
|
|
Disable Liquid Glass, transparency, and animations to save GPU/CPU
|
|
on a headless server:
|
|
|
|
```bash
|
|
defaults write com.apple.universalaccess reduceTransparency -bool true
|
|
defaults write com.apple.universalaccess reduceMotion -bool true
|
|
defaults write NSGlobalDomain NSAutomaticWindowAnimationsEnabled -bool false
|
|
defaults write com.apple.dock launchanim -bool false
|
|
defaults write com.apple.dock expose-animation-duration -float 0.1
|
|
defaults write NSGlobalDomain NSWindowResizeTime -float 0.001
|
|
killall Dock 2>/dev/null || true
|
|
```
|
|
|
|
Note: these are user-level defaults (no sudo needed).
|
|
|
|
## Software Update
|
|
|
|
Keep auto-check, defer auto-install:
|
|
|
|
```bash
|
|
sudo defaults write /Library/Preferences/com.apple.SoftwareUpdate \
|
|
AutomaticInstall -bool false
|
|
```
|