再署名とクラッシュ防止

データ移行後にアプリがクラッシュする理由
macOS のコード署名メカニズム(codesign)は、ファイルパス構造を含むアプリケーションパッケージの完全性を検証します。AppPorts がアプリのデータディレクトリを外部ストレージに移行し、シンボリックリンクに置き換えると、署名シールが壊れ、以下の問題が発生します:
- Gatekeeper ブロック:
codesign --verify --deep --strictが署名失敗を検出し、システムが「破損」または「身元不明の開発者からのアプリ」というダイアログを表示し、アプリの起動をブロック - Keychain アクセスの中断: Keychain アクセスグループに依存するアプリは、署名アイデンティティの変更により保存された認証情報を読み取れなくなる
- Entitlements 失敗: 一部のアプリ Entitlements は署名アイデンティティに紐づいており、署名変更後に Entitlements の不整合が発生
高リスクアプリタイプ
| アプリタイプ | リスクレベル | 理由 |
|---|---|---|
| Sparkle 自動更新アプリ | 高 | アップデーターがアプリを削除または置換し、シンボリックリンクを破損する可能性がある |
| Electron 自動更新アプリ | 高 | electron-updater も外部ストレージ上のアプリに干渉する可能性がある |
| Keychain 依存アプリ | 高 | Ad-hoc 署名により署名アイデンティティが変更され、Keychain アクセスグループが失敗する |
| Mac App Store アプリ | 高 | SIP 保護;再署名不可 |
| ネイティブ自動更新アプリ(Chrome、Edge) | 中 | 自動更新により外部コピーが置換され、ローカルエントリが無効化される可能性がある |
| iOS アプリ(Mac 版) | 低 | Stub Portal または全体シンボリックリンクを使用;署名の問題が少ない |
高リスクデータディレクトリタイプ
| データタイプ | リスクレベル | 理由 |
|---|---|---|
~/Library/Application Support/ | 中 | アプリがファイルロック、SQLite WAL ログ、または拡張属性を使用している可能性があり、シンボリックリンクをまたぐと異常動作する場合がある |
~/Library/Group Containers/ | 中 | 同じ Team 下の複数のアプリで共有されており、シンボリックリンクが他のアプリに干渉する可能性がある |
~/Library/Preferences/ | 低~中 | cfprefsd が plist ファイルをキャッシュしており、シンボリックリンクにより古いデータを読み取る可能性がある |
~/Library/Caches/ | 低 | キャッシュは再構築可能;ほとんどのアプリはキャッシュの不在を適切に処理する |
再署名メカニズム
Ad-hoc 署名
AppPorts は移行後のアプリ署名を修正するためにAd-hoc 署名(証明書なしローカル署名)を使用します。実行コマンド:
codesign --force --deep --sign - <app path>ここで - は Ad-hoc 署名(開発者証明書なし)を示します。
署名フロー
flowchart TD
A[再署名開始] --> B[元の署名アイデンティティをバックアップ]
B --> C{アプリはロックされていますか?}
C -->|はい| D[uchg フラグを一時的に解除]
C -->|いいえ| E{アプリは書き込み可能ですか?}
D --> E
E =>|書き込み不可 & root 所有| F[管理者権限で所有権変更を試行]
E =>|書き込み可能| G[拡張属性をクリーンアップ]
F --> G
F -->|失敗 & MAS アプリ| H[署名をスキップ - SIP 保護]
G --> I[バンドルルートディレクトリの雑多なファイルをクリーンアップ]
I --> J{Contents はシンボリックリンクですか?}
J =>|はい| K[一時的に実ディレクトリコピーに置換]
J =>|いいえ| L[deep 署名を実行]
K --> L
L =>|失敗| M[shallow 署名にフォールバック]
L =>|成功| N{Contents が一時置換されましたか?}
M --> N
N =>|はい| O[シンボリックリンクを復元]
N =>|いいえ| P[uchg フラグを再ロック]
O --> P
P => Q[署名完了]重要なステップ
元の署名アイデンティティのバックアップ: 署名前に、アプリの現在の署名アイデンティティを読み取り(
codesign -dvvでAuthority=行を解析)、~/Library/Application Support/AppPorts/signature-backups/<BundleID>.plistに保存拡張属性のクリーンアップ:
xattr -crを実行してリソースフォーク、Finder 情報などを削除し、署名中の「detritus not allowed」エラーを回避バンドルルートディレクトリのクリーンアップ:
.DS_Store、__MACOSX、.git、.svnなどの雑多なファイルを削除シンボリックリンク Contents の処理:
Contents/がシンボリックリンクの場合(Deep Contents Wrapper 戦略)、一時的に実ディレクトリコピーに置換し、署名後にシンボリックリンクを復元deep 署名 → shallow 署名のフォールバック:
--deep署名(すべてのネストされたコンポーネントをカバー)を優先;権限やリソースフォークの問題で失敗した場合、--deepなしの shallow 署名にフォールバックリトライメカニズム:
codesignが「internal error」を生成するか SIGKILL で終了した場合、最大2回リトライ
署名バックアップと復元
バックアップ
バックアップファイルは ~/Library/Application Support/AppPorts/signature-backups/ ディレクトリに BundleID.plist という名前で保存されます:
| フィールド | 説明 |
|---|---|
bundleIdentifier | アプリの Bundle ID |
signingIdentity | 元の署名アイデンティティ(例:Developer ID Application: ... または ad-hoc) |
originalPath | 元のアプリパス |
backupDate | バックアップのタイムスタンプ |
バックアップは以下のタイミングでトリガーされます:
- データディレクトリ移行前(自動再署名が有効な場合)
- すべての署名操作前(冪等;既存のバックアップは上書きされません)
復元
署名を復元する際、AppPorts はバックアップされた署名アイデンティティに基づいて異なる戦略を実行します:
| バックアップされた署名アイデンティティ | 復元動作 |
|---|---|
ad-hoc または空 | codesign --remove-signature を実行して署名を削除;バックアップを削除 |
| 有効な開発者証明書アイデンティティ | Keychain に証明書が存在するか確認。存在する場合、元のアイデンティティで再署名 |
| 有効な開発者証明書アイデンティティだがこのマシンに証明書がない | Ad-hoc 署名にフォールバック;元の署名は完全には復元できない |
復元失敗のシナリオ
以下のシナリオでは、署名の復元が失敗または不完全になります:
| シナリオ | 結果 |
|---|---|
| バックアップ plist ファイルが存在しない | noBackupFound エラーをスロー;復元不可 |
| 元の開発者証明書がローカル Keychain にない | Ad-hoc 署名にフォールバック。アプリは起動可能だが、Keychain アクセスグループと一部の Entitlements が失敗する可能性がある |
| Mac App Store アプリ(SIP 保護) | サイレントにスキップ。SIP がシステムアプリ署名へのあらゆる変更を防止 |
| アプリディレクトリが書き込み不可 & root 所有 | 管理者権限で所有権変更を試行。ユーザーが認証プロンプトをキャンセルすると失敗 |
| Contents シンボリックリンクターゲットが消失 | 一時置換ステップで copyItem が失敗;署名実行不可 |
| ユーザーが管理者認証をキャンセル | codesignFailed("User cancelled authorization") をスロー |
| deep 署名と shallow 署名の両方が失敗 | エラーが上位に伝播;署名操作失敗 |
⚠️ 開発者証明書の喪失について
最も一般的な実際の復元失敗シナリオは:元のアプリがサードパーティ開発者(例:Developer ID Application: Google LLC)によって署名されているが、現在のマシンの Keychain に対応する秘密鍵がない場合です。この場合、復元操作は Ad-hoc 署名のみを生成でき、元の署名アイデンティティは完全には復元できません。Keychain アクセスグループやエンタープライズ設定プロファイルに特定の署名アイデンティティに依存するアプリでは、機能異常が発生する可能性があります。
