Highlight
ManagedApp フレームワークは iOS/iPadOS 18.4 および visionOS 2.4 で導入されました。MDM から管理対象 app に対して、設定値、パスワード、証明書、そしてハードウェアバインドされたキーをサポートする ACME ID を直接配布でき、変更時には async sequence を通じて app にリアルタイムで通知できます。
主要内容
エンタープライズ環境において、新しくインストールされた app の初回起動は、もっとも体験の悪い瞬間になりがちです。従業員はサーバーアドレス、ユーザー名、パスワード、二要素認証コードを入力し、さらに設定画面で組織のコンプライアンス要件に合わせて項目をひとつずつチェックしていく必要があります。Bob Whiteman はセッション冒頭でこの課題を率直に提示しました(00:51)。手順が増えればミスも増え、従業員は使うのをやめ、IT 部門にはチケットが積み上がり、最終的に組織はその app をリストから外すという結末を迎えます。開発者が自前で解決しようとすれば、ID フェデレーション、設定用 Web サイトの構築、認証局との連携、組織向けカスタムビルドの維持など、本来のコア機能とは無関係な作業を抱え込むことになります。
ManagedApp フレームワークの考え方は、こうした作業をすべてシステム側に委ねることです。管理者が MDM 側で設定を済ませておけば、app は起動した瞬間に必要な構成と鍵を受け取れるため、初回セットアップフローそのものが不要になります(02:25)。フレームワークは 4 種類のデータの安全な配布を一手に担います。app 独自の構成、パスワード、証明書、そして ID(PKCS #12、SCEP、ACME に対応し、ACME ではハードウェアキーへのバインドおよびリモート attestation も可能)です。この仕組みは MDM 側で declarative device management を利用していることを前提としており、app が管理対象になった瞬間から有効になり、すべての MDM 登録タイプをカバーします。
設計上もっとも重要なのは、構成 schema を Apple や MDM ベンダーが共通フォーマットとして定めるのではなく、app 開発者自身が定義するという点です。Bob はこの点を繰り返し強調しています(06:44)。自分の app をもっとも理解しているのは開発者本人であり、もっとも適切な構成可能項目を提示できるのも開発者だけだ、という考え方です。フレームワークは出来合いのソリューションではなく、ソリューションを構築するためのプラットフォームとして位置付けられています。
詳細
ManagedApp フレームワークは 4 つの独立した provider で構成されており、それぞれパスワード、証明書、ID、カスタム構成に対応します(12:20)。前者 3 つはシステム組み込みの型を返し、カスタム構成のデータ構造のみ app 側で自由に決められます。
最初のステップは、カスタム構成の schema を Decodable な型として記述することです。Landmarks のサンプルでは collection というフィールドがひとつあるだけのシンプルな構造になっています。
// Your app's managed configuration
struct LandmarksManagedConfig: Decodable {
private(set) var collection: LandmarkCollection?
private enum CodingKeys: String, CodingKey {
case collection
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
collection = try values.decode(LandmarkCollection.self, forKey: .collection)
}
}
ポイントは次のとおりです。
LandmarksManagedConfigはDecodableに準拠しており、フレームワークはこの型を使って MDM から配布された辞書を Swift の型へデシリアライズします。collectionはprivate(set)で読み取り専用ビューとして公開され、構成はフレームワーク経由でのみ更新されます。CodingKeysでキー名を明示しておき、プロパティ名による暗黙的なマッチングに依存しないようにしています。init(from:)ではdecodeIfPresentではなくdecodeを使っています。フィールドが欠けていればエラーになるため、schema が一致しないケースで app を早めに失敗させられます。
次のステップは、構成を app のランタイムに注入することです。ManagedAppConfigurationProvider は AsyncSequence を返し、構成が変更されるたびにループ本体が再度実行されます(0:02)。
// Receiving the current configuration
import ManagedApp
// [...]
var managedCollection: LandmarkCollection?
// [...]
func loadCollections() {
// [...]
Task {
let configProvider = ManagedAppConfigurationProvider()
for await config in await configProvider.configurations(LandmarksManagedConfig.self) {
// config's type is LandmarksManagedConfig?
managedCollection = config?.collection
} // Loops forever
}
}
ポイントは次のとおりです。
import ManagedAppでフレームワークを取り込みます。モジュール名はそのままManagedAppです。configurations(_:)は schema 型を引数に取り、要素の型はLandmarksManagedConfig?になります。構成が無い場合や、app が非管理状態の場合はnilが流れてきます。for awaitループは終了せず、MDM から新しい構成がプッシュされるたびにイテレーションが回ります。手書きの KVO や NotificationCenter の購読が不要になります。Taskをデータロード処理の中に置くことで、構成の反映とデータロードを同じライフサイクルで扱えます。
3 つめのステップは ID 認証の処理です。VPN 拡張やエンタープライズサービスへの呼び出しではクライアント証明書がよく必要になりますが、フレームワークは SecIdentity をそのまま URLSession の delegate に渡せます。
// Using an identity
final class MyURLSessionDelegate: NSObject, URLSessionDelegate {
func urlSession(_ session: URLSession,
didReceive challenge: URLAuthenticationChallenge)
async -> (URLSession.AuthChallengeDisposition, URLCredential?) {
switch challenge.protectionSpace.authenticationMethod {
case NSURLAuthenticationMethodClientCertificate:
// Look up the identity
let provider = ManagedAppIdentitiesProvider()
let id = "AssetDownloadClient"
guard let identity = try? await provider.identity(withIdentifier: id) else {
// No identity, cancel the challenge
return (.cancelAuthenticationChallenge, nil)
}
// Use the identity to authenticate.
return (.useCredential, URLCredential(identity: identity,
certificates: nil,
persistence: .forSession))
default:
return (.performDefaultHandling, nil)
}
}
}
ポイントは次のとおりです。
URLSessionDelegateの async 版は challenge が到着すると一旦サスペンドし、ID を取得してからコールバックします。ManagedAppIdentitiesProvider().identity(withIdentifier:)は schema で取り決めた識別子を使って ID を取得します。識別子は app 側で自由に定義できます。- ID が取得できない場合は
cancelAuthenticationChallengeを返し、リクエストを弱い認証へフォールバックさせず即座に失敗させます。 URLCredentialのpersistenceには.forSessionを指定することで、秘密鍵が Keychain の外へコピーされることを防ぎます。
ライセンス管理はこのフレームワークがもたらす改善がもっとも分かりやすく現れるユースケースです。Bob は 09:00 で従来のアプローチと比較しています。文字列トークンを配布する方式は漏洩リスクと隣り合わせですが、ManagedApp フレームワークでは組織の CA が発行する暗号 ID を配布でき、秘密鍵はデバイス上で生成され、デバイスから外に出ることはありません。VPN 拡張ではさらに踏み込んで、ハードウェア attestation 付きの ACME ID を取得できるため(10:46)、VPN 認証情報を狙った中間者攻撃の経路を根本から塞げます。
重要ポイント
-
既存の ManagedAppConfiguration を ManagedApp フレームワークへ移行する: 取り組む価値の理由は、旧方式が app 拡張に対応していない、証明書や ID を扱えない、変更通知の仕組みも無いという制約を抱えているためです。新フレームワークは iOS/iPadOS 18.4 以降で、より完全な後継となります(09:46)。始め方としては、まず最小限の schema を定義して「MDM のプッシュ → app が受信する」という経路を疎通させ、その後で旧来の
UserDefaultsからの読み出しロジックを項目単位で移し替え、移し替えるたびに古いコードを削除していくのが扱いやすい進め方です。 -
VPN 拡張にハードウェアバインドされた ACME ID を組み込む: 取り組む価値の理由は、VPN が攻撃対象としてもっとも狙われやすい入口のひとつであり、秘密鍵を Secure Enclave に固定し、リモート attestation を併用することで、組織の VPN アクセス制御ポリシーに本当の意味で信頼性を持たせられるからです(10:30)。始め方としては、schema に ID フィールドを宣言し、MDM 側で ACME 経由の発行を要求し、
NEVPNManagerの認証コールバック内で先ほどのManagedAppIdentitiesProviderのコードを再利用するとスムーズです。 -
ライセンストークンをライセンス ID へ置き換える: 取り組む価値の理由は、トークンは一度漏洩すると組織側でコストを払い続けるしかなくなる一方、デバイス側で生成された鍵ペアはデバイスから出ることがなく、漏洩面がほぼゼロに抑えられるためです(08:30)。始め方としては、まずサーバー側で mTLS をサポートし、証明書発行フローを組織の CA に接続したうえで、クライアント側で
ManagedAppIdentitiesProviderから取得した ID をクライアント証明書認証に利用するとよいでしょう。 -
SSO 拡張でワンタイムパスワードや初期クレデンシャルを受け取る: 取り組む価値の理由は、ID プロバイダ側が独自の認証プロトコルを設計していることが多く、これまでユーザーに手動でクレデンシャルを入力させるしかなかったところを、管理者が MDM 経由で初期パスワードやバインディングトークンを直接プロビジョニングできるようになるからです(11:42)。始め方としては、SSO 拡張に
ManagedAppPasswordsProviderを追加し、起動時に読み出しを試み、値が存在すれば手動入力フローをスキップする実装にするとよいでしょう。 -
構成変更を async sequence で受け取り全体をリロードする: 取り組む価値の理由は、
for awaitループは複数回の更新を素直に扱えるため、フィールドごとに監視することによる中間状態の不整合を避けられるからです(06:00)。始め方としては、Task内でconfigurations(_:)をイテレートし、新しい値を受け取るたびに「構成を反映する」一連の処理を最初から走らせる構成にして、フィールド単位の差分適用を避ける形にするとシンプルです。
関連セッション
- What’s new in Apple device management and identity — デバイス管理と ID 関連の新機能の俯瞰。ManagedApp フレームワークと組み合わせて、デプロイ側まで含めた全体像を把握できます。
- Discover the Enterprise APIs for iOS — エンタープライズ API の拡張機能。ManagedApp フレームワークとは権限モデルの面で補完関係にあります。
- What’s new in privacy — プライバシー境界の変化。管理対象の構成がプライバシーフレームワーク下でどのように安全に配布されるかを理解する助けになります。
- Streamline sign-in with passkey upgrades and credential managers — 認証側の進化。ManagedApp が提供する ID 機能と組み合わせて利用できます。
コメント
GitHub Issues · utterances