Hello - I have added a small bit of additional information at the end. -----Original Message----- From: Theodore Wynnychenko Sent: Friday, April 02, 2021 1:46 PM To: misc@openbsd.org Subject: How I got iked in -current to work with iOS
Hi I had some time today, and decided to send this now. This is how I got OpenBSD's iked daemon (version in current about 3/28/2021) to work with Apple's iOS (iphone/ipad's) version (about) 14.4.2. Some prelude: So, I have no real reason to do this, other than that I want to. I think of it as a learning exercise, so my knowledge is limited in many ways. I got this to work for my specific use case. Specially, I only have a few devices that I want/need to have access to my home's VPN. I do this to have a secure gateway for access to my email/calendar/files/etc when not at home (yes, I host my own services - I know that Apple and Google and other respect my privacy, etc., but ...). I have NOT tried to set this up as a VPN to route all traffic from associated devices (I don't have the a connection with the bandwidth necessary to do so). Because I am only "managing" a handful of devices, I did all of this manually. Limitations/Requirements: Apple's iOS: Recently, Apple has put several restrictions on the certificates iOS devices will accept for ikev2 connections. These are "disclosed" at https://support.apple.com/en-us/HT211025 and https://support.apple.com/en-us/HT210176. Specifically, certificates must: - CA and TLS certs using RSA must use key => 2048 - CA and TLS certs must use SHA-2 - TLS certs must have a SubjectAlternativeName with the DNS name of the server; a CommonName only is not enough For certificates issued after 7/1/2019: - ExtendedKeyUsage for TLS server and TLS client must be included - TLS certs can only be valid for 825 days or less For certificates issued by the Root CA's preinstalled in iOS, after 9/1/2020: - TLS certs can only be valid for 398 days or less OpenBSD's iked: OpenBSD ikev2 also has some specific requirements for certificate authentication. iked requires specific key/authentication combinations: - rfc7427 only supports SHA2-256 (not sure if iOS supports rfc7427) - ecdsa256 only supports P-256 with SHA2-256 - ecdsa382 only supports P-384 with SHA2-384 - ecdsa521 only supports P-521 with SHA2-512 - rsa suppors RSA but only with SHA1 Other/General: In terms of ECDSA certificates, it seems that P-256 and P-384 are the ones that are more generally accepted, and will likely continue to be generally accepted. This appears to be based on NIST's inclusion of them in their "Suite B Cryptography" information. P-521 was not included in "Suite B," and it seems some things have not included support for it. So, I concluded, to be safe (and since it seems the computational/security cost/benefit of P-384 vs P-521 is narrow), and based on the requirements above, to use P-384 with SHA2-384. My setup: Certificates: In order to do this, I used openssl commands directly. I did not use ikectl to create certs or the CA. Before I go into details, I wanted to have certs that would last longer than the Apple limit. Therefore, I needed to have a CA certificate, and TLS certs, that had a "Not Before" date before 7/1/2019. In order to do this, when I created my CA certificate, I changed the time/date on the system using "date 201901010000" before creating the CA, and then reset the date when I was done. Creating back-dated TLS certs is a bit more direct, all that is necessary is to add "-startdate 20190102000000Z" to the openssl ca command when signing the TLS certificate. Obviously, you need to have complete control of the CA (and not care that you are doing this) to accomplish this and get certificates with a longer time horizon for iOS. So, first I created a CA using ECDSA384: I created/edited the openssl.cnf file to have the appropriate additions/defaults I need for these certificates. I will not cover everything, but I think these are the basic requirements (I have edited out many things in the actual file I used, but I THINK what is left is all that may be really needed, but my openssl knowledge is not absolute, and I may have errors that I don't realize): (NOTE: When I was trying to get this to work, I began to believe that the current iOS has a problem with "-" [hyphens] in the CN/SAN, so I did not use them. I now am not sure if "-"'s will work or not.] General defaults for generating the signing request (csr), openssl.cnf: default_bits = 4096 default_keyfile = key.pem default_md = sha384 string_mask = utf8only distinguished_name = req_distinguished_name attributes = req_attributes The distinguished name/attributes are basically from the default cnf file. For the CA cert, openssl.cnf: basicConstraints = CA:TRUE subjectKeyIdentifier = hash authorityKeyIdentifier = keyid:always,issuer:always issuerAltName = issuer:copy For the TLS certs, when creating the signing request, openssl.cnf: basicConstraints = CA:FALSE subjectKeyIdentifier = hash authorityKeyIdentifier = keyid:always,issuer:always subjectAltName = email:move For the TLS certs, when signing with CA, openssl.cnf: default_days = 390 # Most restrictive days for iOS default_md = sha384 preserve = no email_in_dn = no policy = policy_match copy_extensions = copy extendedKeyUsage = serverAuth,clientAuth subjectAltName = DNS:same.as.CN,email:n...@domain.tld In the steps that follow, the openssl.cnf file will need to be adjusted to match each certificate (both CA and TLS) as they are created. Create a CA key - I did this in two steps here, later only one: openssl ecparam -name secp384r1 -out secp384r1.param openssl ecparam -in secp384r1.param -genkey -out ca.ecdsa.key.nopass I wanted a password for the CA key, so, openssl ec -aes256 -in ca.ecdsa.key.clear -out ca.ecdsa.key.pem and then deleted the unencrypted key. Then, create the CA self-signed certificate: openssl req -new -x509 -key ca.ecdsa.key.pem -out cacert.pem -days ### -config openssl.cnf Now, there is a CA certificate and key with ECDSA384 and SHA2-384. Create the TLS server cert and key (only one step this time): openssl ecparam -genkey -name secp384r1 -out server.key Make the csr: openssl req -nodes -new -key ikev2.key -out server.csr -config openssl.cnf Sign the csr with the CA: openssl ca -in server.csr -out server.pem -config openssl.cnf Now, create a client cert the same way, but one extra step. First, create the key, create the CSR, and sign it to get the certificate. But, this has to be bundled in to a .p12 file for import to iOS: openssl pkcs12 -export -in client.pem -inkey client.key -CAfile cacert.pem -out client.p12 Now, there is a cacert.pem (the CA certificate), a server.pem and server.key (the iked daemon's certificates), and a .p12 with the iOS client's certificate and key, and the ca certificate (even though I also import the cacert directly into iOS as a der file). Move the cacert.pem, server.pem, and server.key into the appropriate places for iked to find them. Setup IKED: If I recall, the PF setup I use just basically follows the guidance in the man page (I did this years ago). In my case, it looks like this: pass in on $ext_if proto udp from any to $ext.ip port { isakmp, ipsec-nat-t } pass out on $ext_if proto udp from $ext.ip to any port { isakmp, ipsec-nat-t } pass in on $ext_if proto udp from any to $local_net port { isakmp, ipsec-nat-t } pass out on $ext_if proto udp from $local_net to any port { isakmp, ipsec-nat-t } pass in on $ext_if proto esp from any to $ext.ip pass out on $ext_if proto esp from $ext.ip to any pass in on enc0 proto ipencap from any to $ext.ip keep state (if-bound) pass out on enc0 proto ipencap from $ext.ip to any keep state (if-bound) pass in on enc0 from <ip_ assigned_by_iked> to any keep state (if-bound) pass out on enc0 from any to < ip_ assigned_by_iked > keep state (if-bound) I don't use NAT, since I only have a handful of devices, and each gets assigned a unique IP address by iked. Each device has its own specific entry in iked, and they look like this: ikev2 "client_1" passive esp \ from $local_net to $vpn_net \ local $local_gw peer any \ srcid $server_id dstid $client_id \ ikelifetime 30m lifetime 30m \ ecdsa384 \ config address aaa.bbb.ccc.ddd \ config netmask 255.255.255.0 \ config name-server aaa.bbb.ccc.111 \ config name-server aaa.bbb.ccc.222 local_net = my internal network block vpn_net = network block assigned by iked to clients local_gw = FQDN of local endpoint server_id = CN/SAN of server as listed in certificate created client_id = CN/SAN of client as listed in certificate created I use iked to give each client a specific and unique IP address, and they all fall into the "vpn_net" netblock. I also "config" DNS servers. But, I don't think iOS accepts these (as I will get to later), but I never bothered removing them to see if it would still work. So, each client has a policy in iked that matches the CN/SAN in its certificate, and iked also uses the CN/SAN of the server as the srcid for the policy. Finally, setting up the iOS client: In order to do this I use profiles that get imported in the iOS device. The profiles are generated by Apple Configurator 2 (which requires a Mac), but then also edited them directly (I will explain why later). I guess you don't really need the Apple software, as all AC2 does is generate an XML file. All the parameters in AC2 (and others that are not available in AC2) are published by Apple in the "Configuration Profile Reference." So, I guess this could be created by hand, but I choose not to. (I am not looking at the AC2 pages as I type this, so if I make a slight misstatement, sorry.) To create the profile, there are three sections to complete. First, I filled out the mandatory General tab. In the Certificates tab, I added the cacert.der (I converted the .pem to a .der for Apple, but I think it would accept the .pem if you change the file extension to .crt - maybe?) to the profile. I then added the client.p12 to the profile, also entering the password given to the .p12 when creating it. Finally, in the VPN tab, I set up the ikev2 parameters. Specially: Connection Type: IKEv2 Server: FQDN of my server Remote Identifier: the CN/SAN from the server certificate Local Identifier: the CN/SAN from the client certificate Machine Authentication: Certificate Identity Certificate: choose the certificate you added Certificate type: ECDSA384 Server certificate issuer common name: CA certificate CN ***** THIS IS REQUIRED ***** ***** EVEN THOUGH AC2 LISTS THIS FIELD AS ONLY REQUIRED FOR EAP, THE REFERENCE GUIDE SPECIFICALLY STATES THAT IT IS REQUIRED WHEN A CERTIFICATE TYPE IS SELECTED (This was the last thing I changed before getting it to work - without this set, iOS gives a cryptic "User authentication failed" error and will not connect, while AT THE SAME TIME, IKED shows the connection state changing to VALID and waits for additional traffic - This was very frustrating.) ***** Server Certificate common name: "optional," but is it really (see above), so I entered the TLS certificate's CN/SAN explicitly here Enable EAP: NOT SELECTED Disconnect on Idle: NEVER Dead peer detection rate: HIGH Disable redirects: NOT SELECTED Disable Mobility (MOBIKE): SELECTED Use IP4/IP6 Subnet attributes: SELECTED Enable PFS: NOT SELECTED Enable Cert Revoke check: NOT SELECTED For both IKE SA and child SA parameters, I used: AES-256 SHA2-384 DH Group 20 ***** I ONLY SELECTED THESE SETTINGS HERE ***** ***** When I tried to set arguments for this in iked.conf, the connection would not work, so I did not set ANY specifics for the connection in iked.conf ***** Proxy Server URL: None -------------------- Now, in my situation I also added information for DNS servers and a VPN on demand setup. Years ago, the DNS settings had to be done by hand to the xml file, but now they are included in AC2. I need this because the iOS client needs to direct any DNS queries for my personal domain to internal DNS servers over the tunnel. Without setting the DNS in the profile, a VPN connection comes up, but, unless everything on the iOS device is set with IP address, the internal FQDN's will not resolve using public DNS servers. So, in my case, in AC2, I added: DNS Server Addresses: internal IP's of my DNS servers Domain Name: mypersonaldn.tld DNS Search Domains: mypersonaldn.tld DNS Supplemental Match Domains: mypersonaldn.tld Include Supplemental Domains...: SELECTED These settings direct iOS to send DNS queries for my personal domain through the tunnel to my internal DNS servers for resolution. In my case, the VPN is only used for traffic to my internal services, and all other traffic on the iOS device is routed directly by it to whatever gateway the device is connected to at the time. Finally, I wanted to VPN connection to come up automatically when trying to check (for example) email (the VPN is not "always on"). So, this requires a direct editing of the .mobileconfig xml file. In my case, I added "OnDemand" settings to the "IKEv2" dictionary. The Apple Reference describes the options for this. For my situation, I added the following (I don't necessarily remember all the reasons of exactly why I did it like this): <key>OnDemandEnabled</key> <integer>1</integer> <key>OnDemandRules</key> <array> <dict> <key>Action</key> <string>Disconnect</string> <key>SSIDMatch</key> <array> <string>PersonalSSID1</string> <string>PersonalSSID2</string> </array> </dict> <dict> <key>Action</key> <string>EvaluateConnection</string> <key>ActionParameters</key> <array> <dict> <key>DomainAction</key> <string>ConnectIfNeeded</string> <key>Domains</key> <array> <string> mypersonaldn.tld</string> <string>*. mypersonaldn.tld</string> <string>*.sub. mypersonaldn.tld</string> </array> <key>RequiredDNSServers</key> <array> <string>aaa.bbb.ccc.dd1</string> <string>aaa.bbb.ccc.dd2</string> </array> </dict> </array> </dict> <dict> <key>Action</key> <string>Connect</string> </dict> </array> This addition keeps the VPN from starting when I am at home and connected directly to an AP with a specific SSID. But, when not connected to that AP, any time a request is made for a location in my domain, the VPN comes up automatically. And that's it. I now have iOS devices running the current iOS (14.whatever) that will automatically connect to the -current OpenBSD iked daemon and bring up a VPN to my home network whenever I decide to check email (or other things). I also backdated my CA and other certs, so I am not limited by Apple's certificate time constraints (in my situation, I see no problem with certs that are valid for more than 3 years - after all, if a family member looses a phone, I just delete that entry from iked.conf, and who cares how long the certificate is valid, I am more unhappy about the lost phone). I know this has been a long email. I hope that this helps someone. If there is something that needs to be clarified or explained better, please let me know, and I will try to explain better. Ted ------------------ FOLLOWUP: I was thinking about this, and wanted to mention one other thing. I did not include this in the original message, because I really do not believe it is important/makes a difference. As I was trying to get this to work, I made a number of changes to my configurations/certificates. Some of the things I added to my certificates were CRL and OCSP extensions. I thought maybe Apple's iOS was looking for this information in certificates, and that was why it was failing. Now, I never generated a CRL for this CA, nor do I have a running OCSP server. I just put "placeholder" entries in the certificates. It did not seem to matter, so I did not mention it yesterday. But, I realized that the final certificates I created that work contain these extensions. I did not bother recreating certificates without these extensions once I got it working. I really don't think this matters to Apple's iOS, but I am wrong all the time. So, if the steps above don't work, consider adding this additional extension information to the CA, TLS, and client certificates. In my case, I have added (to my openssl.cnf file): authorityInfoAccess = OCSP;URI:http://ocsp.mydomain.tld:2560 and crlDistributionPoints = crldp1_section [crldp1_section] fullname=URI:https://crl.mydomain.tld/mydomain.ca.crl CRLissuer=dirName:issuer_sect [issuer_sect] C=US O=OrgName CN=CAcertCN Just wanted to mention this in case it matters. But, I really don't think it does. Ted