iOS 앱 안에 VPN 화면을 만들면 첫 번째 놀라움은 설정 앱의 프로토콜 목록이 API 목록이 아니라는 점입니다. Network Extension 프레임워크는 앱이 그 앱이 소유하는 개인 VPN 구성 하나를 만들도록 허용하며, 몇 가지 프로토콜 객체만 받아들입니다. 사람들이 기대하는 나머지 모든 것, L2TP에서 내려받은 프로파일까지는 그 범위 밖에 있거나 Apple의 비공개입니다. 뒤따르는 실패는 대개 버그가 아니라, API가 한 번도 받은 적 없는 작업을 거부하는 것입니다.

신화: API는 설정 편집기다

iOS가 IKEv1, IKEv2, L2TP, IPsec 프로파일을 구성할 수 있으니 앱도 그것들을 프로그램으로 만들 수 있다는 믿음이 있습니다. 만들 수 없습니다. 프레임워크는 호출하는 앱에 정확히 구성 슬롯 하나를 노출하고, 그 슬롯은 공개 프로토콜 클래스의 작은 집합만 받아들입니다. 사람들이 기대하는 나머지 모든 것, L2TP에서 내려받은 프로파일까지는 그 범위 밖에 있거나 Apple의 비공개입니다.

뒤따르는 실패는 대개 버그가 아닙니다. API가 한 번도 받은 적 없는 작업을 거부하는 것이며, 해결책은 API가 소유한 것을 중심으로 화면을 설계하는 것입니다.

세 가지 프로토콜 객체와 존재하지 않는 것들

선택지는 NEVPNProtocolIPSec를 통한 IKEv1, NEVPNProtocolIKEv2를 통한 IKEv2, 그리고 자체 패킷 터널 확장을 배포한다면 NETunnelProviderProtocol입니다. NEVPNManager 참조는 허용되는 프로토콜 객체를 나열합니다. L2TP와 PPTP는 Apple이 그들을 위한 클래스를 공개하지 않았기 때문에 그 목록에 절대 나타나지 않습니다. 둘 중 하나를 시도하면 저장 시 오류로 끝나거나, 더 나쁘게는 시스템이 조용히 사용을 거부하는 구성이 됩니다.

데스크톱 코드는 다른 규칙을 따릅니다. macOS에서 SCNetworkInterfaceCreateWithInterface는 IPv4 위의 L2TP 같은 인터페이스 유형을 쌓아 네트워크 서비스에 연결하고, networksetup -connectpppoeservice가 그 결과를 시작합니다. 공유 비밀과 암호가 시스템 키체인으로 들어가기 때문에 관리자 권한과 도우미 도구가 필요합니다. 그 설계를 iOS로 복사하면 아무것도 컴파일되지 않습니다.

암호가 아니라 키체인 참조를 전달하세요

어느 IKE 클래스도 암호를 텍스트로 받지 않습니다. passwordReference와 sharedSecretReference 속성은 영구 키체인 참조를 원하며, kSecReturnPersistentRef를 요청하여 SecItemCopyMatching에서 얻습니다. 항목 데이터를 전달하거나 필드에 문자열을 넣으면 저장 실패나 빈 자격 증명의 연결이 생깁니다. 참조는 그 뒤의 키체인 항목만큼만 지속되므로 구성이 조립되기 전에 항목이 존재해야 합니다. 저장과 같은 트랜잭션에서 그것을 만들면 순서 관련 버그 한 부류를 통째로 없앨 수 있습니다.

인증서 인증은 다른 경로를 갑니다: PKCS#12 블롭에는 identityData와 identityDataPassword를 씁니다. 그 블롭을 앱 번들 안에 넣어 배포하는 것은 출시 결정이 아니라 샘플 코드에서 물려받은 습관입니다, 왜냐하면 암호가 함께 이동하고 앱의 모든 사본이 같은 신원을 지니기 때문입니다.

권한과 가용성이 모든 것을 가로막습니다

관리자가 아예 없을 수도 있으며, 이유는 프로비저닝입니다. 앱 식별자에는 Network Extension 기능과 개인 VPN 권한이 모두 필요하고, 빌드에 서명하는 프로파일도 둘 다 지녀야 합니다.

이를 건너뛰면 공유 인스턴스가 nil을 반환하거나, 권한을 전혀 언급하지 않는 오류로 저장이 실패합니다. 가용성은 또 다른 관문입니다. iOS 8보다 오래된 것을 대상으로 하면 런타임에 심볼이 없으므로 실패는 충돌이나 nil 관리자이고, 구성 없는 관리자가 아닙니다. 객체 그래프를 디버그하기 전에 런타임과 권한을 확인하세요.

로드하고, 수정하고, 저장한 다음 온디맨드 순서를 보세요

수명 주기를 로드, 수정, 저장으로 다루고 로드를 절대 건너뛰지 마세요. loadFromPreferences이 완료될 때까지 관리자는 아무것도 보유하지 않으며 프로토콜 객체를 읽거나 바꿀 수 없습니다.

두 번째 저장은 구성을 추가하는 대신 대체하므로, 절반만 끝난 저장은 작동하던 프로파일을 손상시킬 수 있습니다. 온디맨드 규칙은 정렬된 배열을 이루고, 그 순서가 곧 정책입니다. 어디서나 연결 규칙을 연결 해제 규칙 앞에 두면 수동 연결 해제 직후 즉시 재연결이 일어나는데, 결함처럼 보이지만 문서화된 평가를 따르는 것입니다.

사용자 동작이 이겨야 할 때는 연결 해제 규칙을 맨 앞으로 옮기세요. 앱의 통제 밖에 남는 것도 똑같이 중요합니다. 경로는 당신이 설정한 환경설정이 아니라 터널이나 서버 정책에서 오며, 패킷 터널 제공자가 시작할 때 이를 보고합니다. 앱은 다른 앱이나 구성 프로파일이 설치한 프로파일을 나열하거나 편집할 수 없습니다, 그러니 기기의 모든 VPN을 보여 주는 화면을 약속하지 마세요.

관련 글