Профіль VPN у iOS: passwordReference, а не пароль
Посилання на зв’язку ключів — це дескриптор, а не секрет. Передавання байтів пояснює помилку -50, збій після перезавантаження й пастку ротації в збережених профілях VPN.
Ви встановлюєте профіль, тунель піднімається, і все виглядає завершеним. Потім телефон перезавантажується, on-demand будить з’єднання без застосунку на передньому плані, і тунель відмовляється запускатися. Профіль досі на місці, пароль досі правильний, і саме тому цей збій відправляє людей шукати не там. Network Extension не може прочитати ваш секрет із профілю: йому потрібне посилання на зв’язку ключів, яке він здатен розв’язати сам.
Розбір нижче простежує одну спробу з’єднання крізь цю передачу — від запиту, що будує посилання, до перезавантаження, яке його перевіряє.
Профіль зберігає дескриптор, а не секрет
NEVPNProtocol відкриває passwordReference і sharedSecretReference недарма. Коли демон запускає тунель, він може працювати не всередині вашого процесу, тож не може викликати ваш код і попросити пароль. Він читає постійне посилання на зв’язку ключів, розв’язує його у власному контексті й сам дістає елемент.
Ви будуєте це посилання, запитуючи його у зв’язки ключів; це інший запит, ніж запит даних:
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: "vpn-credentials",
kSecAttrAccount as String: "user",
kSecReturnPersistentRef as String: true,
kSecMatchLimit as String: kSecMatchLimitOne
]Передайте протоколу повернутий об’єкт, а не дані елемента. На практиці профіль записує, де живе секрет, тому резервна копія конфігурації не копіює пароль мовчки в plist.
Чому API відповідає -50
errSecParam означає, що запит або значення були неправильно сформовані для операції, і це помилка, яку найчастіше бачать, коли запити посилання й даних плутаються. Запит даних, коли код-одержувач очікує посилання, присвоєння сирих байтів полю passwordReference або запит із групою доступу, яку застосунок не може використати, — усе це її породжує.
Обгорткові бібліотеки до цього спонукають. Помічник, що повертає байти, не взаємозамінний із тим, що повертає посилання, тому виправлення зазвичай полягає в іншому запиті, а не в іншому паролі.
Та сама невідповідність пояснює профіль, який зберігається без скарг, а потім не підключається: збережено блоб, а не дескриптор, і демонові нема чого розв’язувати.
Зберігається добре, падає під час підключення
Збереження задіює контекст вашого застосунку. Підключення задіює контекст демона, і доступ у них не однаковий. Тож профіль може пройти всі тести в симуляторі й упасти на першому запуску on-demand.
Така сама форма симптома після перезавантаження: працює, поки застосунок відкритий, і вмирає, коли система запускає тунель сама.
Перевірте фоновий шлях навмисно перед випуском. Викличте on-demand з’єднання із закритим застосунком, а потім читайте консоль пристрою, а не журнал застосунку, бо збій записує процес, який намагався розв’язати посилання. Якщо консоль показує, що демон відхиляє елемент, посилання гаразд, а проблема в доступності елемента.
Доступність вирішує, чи спрацює перезавантаження
Елемент має бути читабельним для будь-якого системного процесу, що запускає тунель, а це ширша авдиторія, ніж ваш застосунок на передньому плані. Це обмеження саме обирає атрибут доступності. Елемент, захищений до першого розблокування, доступний для фонового запуску після того, як пристрій розблокували один раз; елемент, що вимагає розблокованого пристрою просто зараз, — ні.
Обрати суворіший клас спокусливо, і саме це ламає on-demand профіль на заблокованому телефоні.
Після зміни класу створіть елемент заново, а не редагуйте атрибути наявного, потім збережіть протокол із новим посиланням. Зміни атрибутів наявного елемента не завжди дають поведінку, яку передбачає документація, а перестворений елемент знімає сумнів.
Ротація — це місце, де гниють збережені профілі
Зміна пароля у зв’язці ключів автоматично не змінює те, на що вказує збережений профіль. Видалення елемента гірше: профіль зберігає посилання на те, чого вже немає, і збій виглядає як відмова автентифікації.
Ротуйте в одному порядку. Оновіть елемент, запитайте нове постійне посилання, збережіть протокол знову, а потім підключіться із закритим застосунком, щоб довести, що демон досі може його розв’язати. Тримайте пароль користувача й спільний секрет в окремих елементах, щоб кожен ротувався за власним графіком, і ніколи не логуйте словник запиту чи саме посилання. Дескриптор не секрет, але це мапа до нього.