Hello - I have added a small bit of additional information at the end.

-----Original Message-----
From: Theodore Wynnychenko [mailto:t...@uchicago.edu] 
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





Reply via email to