fix: harden macOS signing flow
This commit is contained in:
parent
96ee027371
commit
0b350d78d5
64
apps/macos/README.md
Normal file
64
apps/macos/README.md
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
# Clawdbot macOS app (dev + signing)
|
||||||
|
|
||||||
|
## Quick dev run
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# from repo root
|
||||||
|
scripts/restart-mac.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Options:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
scripts/restart-mac.sh --no-sign # fastest dev; ad-hoc signing (TCC permissions do not stick)
|
||||||
|
scripts/restart-mac.sh --sign # force code signing (requires cert)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Packaging flow
|
||||||
|
|
||||||
|
```bash
|
||||||
|
scripts/package-mac-app.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Creates `dist/Clawdbot.app` and signs it via `scripts/codesign-mac-app.sh`.
|
||||||
|
|
||||||
|
## Signing behavior
|
||||||
|
|
||||||
|
Auto-selects identity (first match):
|
||||||
|
1) Developer ID Application
|
||||||
|
2) Apple Distribution
|
||||||
|
3) Apple Development
|
||||||
|
4) first available identity
|
||||||
|
|
||||||
|
If none found:
|
||||||
|
- errors by default
|
||||||
|
- set `ALLOW_ADHOC_SIGNING=1` or `SIGN_IDENTITY="-"` to ad-hoc sign
|
||||||
|
|
||||||
|
## Team ID audit (Sparkle mismatch guard)
|
||||||
|
|
||||||
|
After signing, we read the app bundle Team ID and compare every Mach-O inside the app.
|
||||||
|
If any embedded binary has a different Team ID, signing fails.
|
||||||
|
|
||||||
|
Skip the audit:
|
||||||
|
```bash
|
||||||
|
SKIP_TEAM_ID_CHECK=1 scripts/package-mac-app.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## Library validation workaround (dev only)
|
||||||
|
|
||||||
|
If Sparkle Team ID mismatch blocks loading (common with Apple Development certs), opt in:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
DISABLE_LIBRARY_VALIDATION=1 scripts/package-mac-app.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
This adds `com.apple.security.cs.disable-library-validation` to app entitlements.
|
||||||
|
Use for local dev only; keep off for release builds.
|
||||||
|
|
||||||
|
## Useful env flags
|
||||||
|
|
||||||
|
- `SIGN_IDENTITY="Apple Development: Your Name (TEAMID)"`
|
||||||
|
- `ALLOW_ADHOC_SIGNING=1` (ad-hoc, TCC permissions do not persist)
|
||||||
|
- `CODESIGN_TIMESTAMP=off` (offline debug)
|
||||||
|
- `DISABLE_LIBRARY_VALIDATION=1` (dev-only Sparkle workaround)
|
||||||
|
- `SKIP_TEAM_ID_CHECK=1` (bypass audit)
|
||||||
@ -32,6 +32,9 @@ To build the macOS app and package it into `dist/Clawdbot.app`, run:
|
|||||||
|
|
||||||
If you don't have an Apple Developer ID certificate, the script will automatically use **ad-hoc signing** (`-`).
|
If you don't have an Apple Developer ID certificate, the script will automatically use **ad-hoc signing** (`-`).
|
||||||
|
|
||||||
|
For dev run modes, signing flags, and Team ID troubleshooting, see the macOS app README:
|
||||||
|
https://github.com/clawdbot/clawdbot/blob/main/apps/macos/README.md
|
||||||
|
|
||||||
> **Note**: Ad-hoc signed apps may trigger security prompts. If the app crashes immediately with "Abort trap 6", see the [Troubleshooting](#troubleshooting) section.
|
> **Note**: Ad-hoc signed apps may trigger security prompts. If the app crashes immediately with "Abort trap 6", see the [Troubleshooting](#troubleshooting) section.
|
||||||
|
|
||||||
## 3. Install the CLI
|
## 3. Install the CLI
|
||||||
|
|||||||
@ -14,6 +14,7 @@ This app is usually built from [`scripts/package-mac-app.sh`](https://github.com
|
|||||||
- inject build metadata into Info.plist: `ClawdbotBuildTimestamp` (UTC) and `ClawdbotGitCommit` (short hash) so the About pane can show build, git, and debug/release channel.
|
- inject build metadata into Info.plist: `ClawdbotBuildTimestamp` (UTC) and `ClawdbotGitCommit` (short hash) so the About pane can show build, git, and debug/release channel.
|
||||||
- **Packaging requires Node 22+**: the script runs TS builds and the Control UI build.
|
- **Packaging requires Node 22+**: the script runs TS builds and the Control UI build.
|
||||||
- reads `SIGN_IDENTITY` from the environment. Add `export SIGN_IDENTITY="Apple Development: Your Name (TEAMID)"` (or your Developer ID Application cert) to your shell rc to always sign with your cert. Ad-hoc signing requires explicit opt-in via `ALLOW_ADHOC_SIGNING=1` or `SIGN_IDENTITY="-"` (not recommended for permission testing).
|
- reads `SIGN_IDENTITY` from the environment. Add `export SIGN_IDENTITY="Apple Development: Your Name (TEAMID)"` (or your Developer ID Application cert) to your shell rc to always sign with your cert. Ad-hoc signing requires explicit opt-in via `ALLOW_ADHOC_SIGNING=1` or `SIGN_IDENTITY="-"` (not recommended for permission testing).
|
||||||
|
- runs a Team ID audit after signing and fails if any Mach-O inside the app bundle is signed by a different Team ID. Set `SKIP_TEAM_ID_CHECK=1` to bypass.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
@ -23,6 +24,7 @@ scripts/package-mac-app.sh # auto-selects identity; errors if none
|
|||||||
SIGN_IDENTITY="Developer ID Application: Your Name" scripts/package-mac-app.sh # real cert
|
SIGN_IDENTITY="Developer ID Application: Your Name" scripts/package-mac-app.sh # real cert
|
||||||
ALLOW_ADHOC_SIGNING=1 scripts/package-mac-app.sh # ad-hoc (permissions will not stick)
|
ALLOW_ADHOC_SIGNING=1 scripts/package-mac-app.sh # ad-hoc (permissions will not stick)
|
||||||
SIGN_IDENTITY="-" scripts/package-mac-app.sh # explicit ad-hoc (same caveat)
|
SIGN_IDENTITY="-" scripts/package-mac-app.sh # explicit ad-hoc (same caveat)
|
||||||
|
DISABLE_LIBRARY_VALIDATION=1 scripts/package-mac-app.sh # dev-only Sparkle Team ID mismatch workaround
|
||||||
```
|
```
|
||||||
|
|
||||||
### Ad-hoc Signing Note
|
### Ad-hoc Signing Note
|
||||||
|
|||||||
@ -4,11 +4,28 @@ set -euo pipefail
|
|||||||
APP_BUNDLE="${1:-dist/Clawdbot.app}"
|
APP_BUNDLE="${1:-dist/Clawdbot.app}"
|
||||||
IDENTITY="${SIGN_IDENTITY:-}"
|
IDENTITY="${SIGN_IDENTITY:-}"
|
||||||
TIMESTAMP_MODE="${CODESIGN_TIMESTAMP:-auto}"
|
TIMESTAMP_MODE="${CODESIGN_TIMESTAMP:-auto}"
|
||||||
|
DISABLE_LIBRARY_VALIDATION="${DISABLE_LIBRARY_VALIDATION:-0}"
|
||||||
|
SKIP_TEAM_ID_CHECK="${SKIP_TEAM_ID_CHECK:-0}"
|
||||||
ENT_TMP_BASE=$(mktemp -t clawdbot-entitlements-base.XXXXXX)
|
ENT_TMP_BASE=$(mktemp -t clawdbot-entitlements-base.XXXXXX)
|
||||||
ENT_TMP_APP=$(mktemp -t clawdbot-entitlements-app.XXXXXX)
|
ENT_TMP_APP=$(mktemp -t clawdbot-entitlements-app.XXXXXX)
|
||||||
ENT_TMP_APP_BASE=$(mktemp -t clawdbot-entitlements-app-base.XXXXXX)
|
ENT_TMP_APP_BASE=$(mktemp -t clawdbot-entitlements-app-base.XXXXXX)
|
||||||
ENT_TMP_RUNTIME=$(mktemp -t clawdbot-entitlements-runtime.XXXXXX)
|
ENT_TMP_RUNTIME=$(mktemp -t clawdbot-entitlements-runtime.XXXXXX)
|
||||||
|
|
||||||
|
if [[ "${APP_BUNDLE}" == "--help" || "${APP_BUNDLE}" == "-h" ]]; then
|
||||||
|
cat <<'HELP'
|
||||||
|
Usage: scripts/codesign-mac-app.sh [app-bundle]
|
||||||
|
|
||||||
|
Env:
|
||||||
|
SIGN_IDENTITY="Apple Development: Your Name (TEAMID)"
|
||||||
|
ALLOW_ADHOC_SIGNING=1
|
||||||
|
CODESIGN_TIMESTAMP=auto|on|off
|
||||||
|
DISABLE_LIBRARY_VALIDATION=1 # dev-only Sparkle Team ID workaround
|
||||||
|
SKIP_TEAM_ID_CHECK=1 # bypass Team ID audit
|
||||||
|
ENABLE_TIME_SENSITIVE_NOTIFICATIONS=1
|
||||||
|
HELP
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
if [ ! -d "$APP_BUNDLE" ]; then
|
if [ ! -d "$APP_BUNDLE" ]; then
|
||||||
echo "App bundle not found: $APP_BUNDLE" >&2
|
echo "App bundle not found: $APP_BUNDLE" >&2
|
||||||
exit 1
|
exit 1
|
||||||
@ -184,6 +201,14 @@ cat > "$ENT_TMP_APP" <<'PLIST'
|
|||||||
</plist>
|
</plist>
|
||||||
PLIST
|
PLIST
|
||||||
|
|
||||||
|
if [[ "$DISABLE_LIBRARY_VALIDATION" == "1" ]]; then
|
||||||
|
/usr/libexec/PlistBuddy -c "Add :com.apple.security.cs.disable-library-validation bool true" "$ENT_TMP_APP_BASE" >/dev/null 2>&1 || \
|
||||||
|
/usr/libexec/PlistBuddy -c "Set :com.apple.security.cs.disable-library-validation true" "$ENT_TMP_APP_BASE"
|
||||||
|
/usr/libexec/PlistBuddy -c "Add :com.apple.security.cs.disable-library-validation bool true" "$ENT_TMP_APP" >/dev/null 2>&1 || \
|
||||||
|
/usr/libexec/PlistBuddy -c "Set :com.apple.security.cs.disable-library-validation true" "$ENT_TMP_APP"
|
||||||
|
echo "Note: disable-library-validation entitlement enabled (DISABLE_LIBRARY_VALIDATION=1)."
|
||||||
|
fi
|
||||||
|
|
||||||
# The time-sensitive entitlement is restricted and requires explicit enablement
|
# The time-sensitive entitlement is restricted and requires explicit enablement
|
||||||
# (and typically a matching provisioning profile). It is *not* safe to enable
|
# (and typically a matching provisioning profile). It is *not* safe to enable
|
||||||
# unconditionally for local debug packaging since AMFI will refuse to launch.
|
# unconditionally for local debug packaging since AMFI will refuse to launch.
|
||||||
@ -209,6 +234,51 @@ sign_plain_item() {
|
|||||||
codesign --force ${options_args+"${options_args[@]}"} "${timestamp_args[@]}" --sign "$IDENTITY" "$target"
|
codesign --force ${options_args+"${options_args[@]}"} "${timestamp_args[@]}" --sign "$IDENTITY" "$target"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
team_id_for() {
|
||||||
|
codesign -dv --verbose=4 "$1" 2>&1 | awk -F= '/^TeamIdentifier=/{print $2; exit}'
|
||||||
|
}
|
||||||
|
|
||||||
|
verify_team_ids() {
|
||||||
|
if [[ "$SKIP_TEAM_ID_CHECK" == "1" ]]; then
|
||||||
|
echo "Note: skipping Team ID audit (SKIP_TEAM_ID_CHECK=1)."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local expected
|
||||||
|
expected="$(team_id_for "$APP_BUNDLE" || true)"
|
||||||
|
if [[ -z "$expected" ]]; then
|
||||||
|
echo "WARN: TeamIdentifier missing on app bundle; skipping Team ID audit."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local mismatches=()
|
||||||
|
while IFS= read -r -d '' f; do
|
||||||
|
if /usr/bin/file "$f" | /usr/bin/grep -q "Mach-O"; then
|
||||||
|
local team
|
||||||
|
team="$(team_id_for "$f" || true)"
|
||||||
|
if [[ -z "$team" ]]; then
|
||||||
|
team="not set"
|
||||||
|
fi
|
||||||
|
if [[ "$expected" == "not set" ]]; then
|
||||||
|
if [[ "$team" != "not set" ]]; then
|
||||||
|
mismatches+=("$f (TeamIdentifier=$team)")
|
||||||
|
fi
|
||||||
|
elif [[ "$team" != "$expected" ]]; then
|
||||||
|
mismatches+=("$f (TeamIdentifier=$team)")
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done < <(find "$APP_BUNDLE" -type f -print0)
|
||||||
|
|
||||||
|
if [[ "${#mismatches[@]}" -gt 0 ]]; then
|
||||||
|
echo "ERROR: Team ID mismatch detected (expected: $expected)"
|
||||||
|
for entry in "${mismatches[@]}"; do
|
||||||
|
echo " - $entry"
|
||||||
|
done
|
||||||
|
echo "Hint: re-sign embedded frameworks or set DISABLE_LIBRARY_VALIDATION=1 for dev builds."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# Sign main binary
|
# Sign main binary
|
||||||
if [ -f "$APP_BUNDLE/Contents/MacOS/Clawdbot" ]; then
|
if [ -f "$APP_BUNDLE/Contents/MacOS/Clawdbot" ]; then
|
||||||
echo "Signing main binary"; sign_item "$APP_BUNDLE/Contents/MacOS/Clawdbot" "$APP_ENTITLEMENTS"
|
echo "Signing main binary"; sign_item "$APP_BUNDLE/Contents/MacOS/Clawdbot" "$APP_ENTITLEMENTS"
|
||||||
@ -218,6 +288,11 @@ fi
|
|||||||
SPARKLE="$APP_BUNDLE/Contents/Frameworks/Sparkle.framework"
|
SPARKLE="$APP_BUNDLE/Contents/Frameworks/Sparkle.framework"
|
||||||
if [ -d "$SPARKLE" ]; then
|
if [ -d "$SPARKLE" ]; then
|
||||||
echo "Signing Sparkle framework and helpers"
|
echo "Signing Sparkle framework and helpers"
|
||||||
|
find "$SPARKLE" -type f -print0 | while IFS= read -r -d '' f; do
|
||||||
|
if /usr/bin/file "$f" | /usr/bin/grep -q "Mach-O"; then
|
||||||
|
sign_plain_item "$f"
|
||||||
|
fi
|
||||||
|
done
|
||||||
sign_plain_item "$SPARKLE/Versions/B/Sparkle"
|
sign_plain_item "$SPARKLE/Versions/B/Sparkle"
|
||||||
sign_plain_item "$SPARKLE/Versions/B/Autoupdate"
|
sign_plain_item "$SPARKLE/Versions/B/Autoupdate"
|
||||||
sign_plain_item "$SPARKLE/Versions/B/Updater.app/Contents/MacOS/Updater"
|
sign_plain_item "$SPARKLE/Versions/B/Updater.app/Contents/MacOS/Updater"
|
||||||
@ -240,5 +315,7 @@ fi
|
|||||||
# Finally sign the bundle
|
# Finally sign the bundle
|
||||||
sign_item "$APP_BUNDLE" "$APP_ENTITLEMENTS"
|
sign_item "$APP_BUNDLE" "$APP_ENTITLEMENTS"
|
||||||
|
|
||||||
|
verify_team_ids
|
||||||
|
|
||||||
rm -f "$ENT_TMP_BASE" "$ENT_TMP_APP_BASE" "$ENT_TMP_APP" "$ENT_TMP_RUNTIME"
|
rm -f "$ENT_TMP_BASE" "$ENT_TMP_APP_BASE" "$ENT_TMP_APP" "$ENT_TMP_RUNTIME"
|
||||||
echo "Codesign complete for $APP_BUNDLE"
|
echo "Codesign complete for $APP_BUNDLE"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user