8. Dukungan macOS
Daftar Isi
- 8.1 Komponen di macOS
- 8.2 PKG installer struktur
- 8.3 LaunchDaemon Hermes Helper
- 8.4 LaunchDaemon WireGuard tunnel (per session)
- 8.5 Code signing
- 8.6 Notarization
- 8.7 IPC: unix-socket
- 8.8 Privacy permissions (TCC)
- 8.9 Build matrix
- 8.10 Testing checklist macOS
- 8.11 Common issues
8.1 Komponen di macOS
Komponen yang ter-distribute via satu .pkg installer:
| Komponen | Lokasi | Run as |
|---|---|---|
| Hermes UI app | /Applications/HermesNetwork360Guard.app |
Current user |
| Hermes Helper Service | /usr/local/bin/HermesHelperSvc + LaunchDaemon |
root |
WireGuard wireguard-go (embedded) |
HermesNetwork360Guard.app/Contents/MacOS/{arm,intel}/wireguard-go |
(called by Helper) |
wg-quick script |
/usr/local/bin/wg-quick (di-install oleh installer atau dependency wireguard-tools) |
(called by Helper) |
PENTING:
wireguard-gosudah embedded di app bundle Hermes Guard (sesuai pattern existing — lihatHermes360-MacOS-Installer/HermesNetwork/Contents/MacOS/{arm,intel}/wireguard-go). User TIDAK perlu install WireGuard official client dariwireguard.comataubrew install wireguard-tools. Refactor mempertahankan embedded approach ini.
8.2 PKG installer struktur
Hermes-Network-360-Guard.pkg
└── Payload/
├── Applications/HermesNetwork360Guard.app/
│ └── Contents/MacOS/
│ ├── HermesNetwork360Guard ← UI binary (universal)
│ ├── arm/wireguard-go ← BUNDLED ARM64
│ └── intel/wireguard-go ← BUNDLED Intel
├── usr/local/bin/
│ ├── HermesHelperSvc ← Helper Service
│ └── wg-quick ← script wrapper (bundled atau dependency)
└── Library/LaunchDaemons/
└── com.hermesnetwork.helper.plist
scripts/postinstall:
#!/bin/bash
set -e
# Permission untuk Helper
chown root:wheel /usr/local/bin/HermesHelperSvc
chmod 755 /usr/local/bin/HermesHelperSvc
# Permission untuk wg-quick (kalau di-bundle di /usr/local/bin/)
[ -f /usr/local/bin/wg-quick ] && chmod 755 /usr/local/bin/wg-quick
# wireguard-go BUNDLED di app bundle, signed sebagai bagian dari .app —
# tidak perlu copy / chmod terpisah. Helper resolve path lewat
# /Applications/HermesNetwork360Guard.app/Contents/MacOS/{arm,intel}/wireguard-go
# berdasarkan arsitektur runtime.
# LaunchDaemon plist
chown root:wheel /Library/LaunchDaemons/com.hermesnetwork.helper.plist
chmod 644 /Library/LaunchDaemons/com.hermesnetwork.helper.plist
# Buat directory wireguard
mkdir -p /etc/wireguard
chmod 700 /etc/wireguard
chown root:wheel /etc/wireguard
# Bootstrap Helper LaunchDaemon
launchctl bootstrap system /Library/LaunchDaemons/com.hermesnetwork.helper.plist
exit 0
8.3 LaunchDaemon Hermes Helper
/Library/LaunchDaemons/com.hermesnetwork.helper.plist:
<?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>Label</key>
<string>com.hermesnetwork.helper</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/HermesHelperSvc</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/var/log/hermes-helper.out.log</string>
<key>StandardErrorPath</key>
<string>/var/log/hermes-helper.err.log</string>
<key>UserName</key>
<string>root</string>
<key>GroupName</key>
<string>wheel</string>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
</dict>
</dict>
</plist>
Manage:
# Lifecycle
sudo launchctl bootstrap system /Library/LaunchDaemons/com.hermesnetwork.helper.plist
sudo launchctl bootout system/com.hermesnetwork.helper
sudo launchctl kickstart -k system/com.hermesnetwork.helper
# Status
sudo launchctl list com.hermesnetwork.helper
8.4 LaunchDaemon WireGuard tunnel (per session)
Saat Helper apply config, dia create LaunchDaemon kedua untuk tunnel itu sendiri (lihat MacBackend di Bab 4 §4.3.4):
/Library/LaunchDaemons/com.hermesnetwork.sase.Hermes.plist:
<?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>Label</key>
<string>com.hermesnetwork.sase.Hermes</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/wg-quick</string>
<string>up</string>
<string>Hermes</string>
</array>
<key>RunAtLoad</key><true/>
<key>KeepAlive</key><false/>
<key>UserName</key><string>root</string>
</dict>
</plist>
Helper sebenarnya tidak harus pakai LaunchDaemon untuk tunnel — bisa langsung
wg-quick upsaat di-request. Pakai LaunchDaemon kalau ingin auto-start saat reboot tanpa harus klik Connect.
8.5 Code signing
Sama seperti pattern umum:
APP_PATH="bin/Release/net8.0/osx-arm64/publish/HermesNetwork360Guard.app"
SIGN_ID="Developer ID Application: Hermes Network Inc. (XXXXXXXXXX)"
# Sign Helper binary terlebih dulu
codesign --force --options runtime --sign "$SIGN_ID" --timestamp \
"publish/HermesHelperSvc"
# Sign WG binaries
for bin in wg-quick; do # wireguard-go di-sign bersamaan dengan app bundle (di Contents/MacOS/{arm,intel}/)
codesign --force --options runtime --sign "$SIGN_ID" --timestamp \
"publish/$bin"
done
# Sign UI app + dylibs
find "$APP_PATH" -type f \( -name "*.dylib" -o -name "*.so" -o -perm +111 \) \
-exec codesign --force --options runtime --sign "$SIGN_ID" --timestamp {} \;
codesign --force --options runtime --sign "$SIGN_ID" --timestamp \
--entitlements "Resources/HermesNetwork360Guard.entitlements" \
"$APP_PATH"
Entitlements (HermesNetwork360Guard.entitlements):
<?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>com.apple.security.cs.allow-jit</key><true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key><true/>
<key>com.apple.security.network.client</key><true/>
<key>com.apple.security.network.server</key><false/>
<!-- Tidak butuh apple-events karena tidak osascript -->
</dict>
</plist>
Penting: UI tidak perlu
osascriptprivilege karena operasi privileged sudah lewat Helper (yang jalan as root via LaunchDaemon dari saat install). Ini lebih clean dan aman.
8.6 Notarization
# PKG complete (UI + Helper + WG bins)
pkgbuild --root publish-staging \
--identifier "com.hermesnetwork.guard" \
--version "8.4.0" \
--install-location "/" \
--scripts scripts/ \
HermesNetwork360Guard.component.pkg
productsign --sign "Developer ID Installer: Hermes Network Inc. (XXXXXXXXXX)" \
HermesNetwork360Guard.component.pkg \
HermesNetwork360Guard.pkg
xcrun notarytool submit HermesNetwork360Guard.pkg \
--keychain-profile AC_PASSWORD --wait
xcrun stapler staple HermesNetwork360Guard.pkg
8.7 IPC: unix-socket
Helper listen di /var/run/hermes-helper.sock. Saat startup, Helper:
- Delete socket file existing (kalau ada dari crash sebelumnya)
- Bind socket
chmod 0666(semua user bisa connect) atau0660dengan group khusus
// Di MacBackend / JsonRpcServer setup
File.Delete("/var/run/hermes-helper.sock"); // ignore error
listener.Bind(new UnixDomainSocketEndPoint("/var/run/hermes-helper.sock"));
listener.Listen(64);
// Set permission supaya UI app (running as user) bisa connect
File.SetUnixFileMode("/var/run/hermes-helper.sock",
UnixFileMode.UserRead | UnixFileMode.UserWrite |
UnixFileMode.GroupRead | UnixFileMode.GroupWrite |
UnixFileMode.OtherRead | UnixFileMode.OtherWrite);
Authentication caller via getpeereid — lihat Bab 7 §7.5.2.
8.8 Privacy permissions (TCC)
Hermes UI tidak butuh:
- ❌ Full Disk Access
- ❌ Screen Recording
- ❌ Accessibility
Hermes UI butuh:
- ✅ Network access (otomatis granted untuk app signed)
Helper Service jalan as root, jadi tidak terkena TCC. Ini alasan tambahan kenapa pisah jadi 2 component:
- UI = sandbox-friendly, sedikit permission
- Helper = root-level, di luar TCC scope
8.9 Build matrix
Universal binary (Intel + ARM) untuk Mac:
# UI
dotnet publish -r osx-arm64 --self-contained -c Release -o publish/ui-arm64
dotnet publish -r osx-x64 --self-contained -c Release -o publish/ui-x64
lipo -create publish/ui-arm64/HermesNetwork360Guard \
publish/ui-x64/HermesNetwork360Guard \
-output publish/universal/HermesNetwork360Guard
# Helper
dotnet publish -p:HermesHelperSvc -r osx-arm64 --self-contained -c Release -o publish/helper-arm64
dotnet publish -p:HermesHelperSvc -r osx-x64 --self-contained -c Release -o publish/helper-x64
lipo -create publish/helper-arm64/HermesHelperSvc \
publish/helper-x64/HermesHelperSvc \
-output publish/universal/HermesHelperSvc
8.10 Testing checklist macOS
- Build
.pkgdi mesin Mac - Install di Mac bersih, non-admin user
- Verify Helper running:
sudo launchctl list com.hermesnetwork.helper - Verify socket:
ls -la /var/run/hermes-helper.sock - Login user test di UI → klik Connect SASE
- Verify tunnel up:
wg show - Verify gateway IP:
curl ifconfig.me - Disable WiFi 30s → reconnect → tunnel auto-recover
- Reboot Mac → Helper auto-start, tunnel auto-up (kalau RunAtLoad di plist tunnel)
- Uninstall via app → Helper unloaded, plist dihapus
8.11 Common issues
| Issue | Cause | Fix |
|---|---|---|
launchctl list com.hermesnetwork.helper exit 113 |
Helper belum bootstrap | sudo launchctl bootstrap system /Library/LaunchDaemons/com.hermesnetwork.helper.plist |
Permission denied saat connect socket dari UI |
Mode socket terlalu ketat | chmod 0666 /var/run/hermes-helper.sock di Helper startup |
| Tunnel up tapi tidak ada handshake | Firewall block UDP outbound | Test nc -uvz <gateway> 51820 |
| App freeze saat klik Connect | Helper crash | Cek /var/log/hermes-helper.err.log |
| Notarization gagal “hardened runtime” | Lupa --options runtime |
Re-sign dengan flag |