Access Server External PKI Guide

Overview
--------

The Access Server External PKI (Public Key Infrastructure) feature
allows operation of the Access Server with third-party tools for X509
PKI management, instead of using the built-in certificate management
capabilities.

When configured for external PKI usage, the Access Server will not
manage client certificates directly; instead, the customer's
third-party PKI software will be used to generate and distribute client
certificate/key pairs to client machines, and a server certificate/key
pair to the OpenVPN server.

When managing an Access Server network that is configured for external
PKI usage, distribution of the client must be made over two channels
rather than one:

(1) profile channel -- distribution of the OpenVPN Connect client and
    bundled server-locked profile can be done via normal means --
    either via the Connect web-based interface, or via push-based
    distribution of the client installer MSI/DMG to client machines.

(2) client certificate/key channel -- while the standard Access
    Server (without External PKI enabled) transparently bundles the
    client certificate/key using the profile channel, External PKI
    mode separates this function.  Generation of client
    certificates/keys would be done using the customer's third-party
    tool for PKI management, and installation on client machines would
    be done using the host OS certificate/key store (e.g. Mac Keychain,
    Windows certificate store, or Linux OpenSC).

When operating in External PKI mode, the Access Server only supports
server-locked profiles, not user-locked profiles.  On the client,
the server-locked profile can only be used if a suitable client
certificate/key pair has already been installed into the host OS
certificate/key store (e.g. Mac Keychain, Windows certificate store, or
Linux OpenSC).

The Access Server Connect client does NOT require direct access to the
private key, as it is capable of performing RSA operations on the
key via the CSP (cryptographic service provider) API provided by the
host OS certificate/key store.  This is a beneficial security
feature, as it allows the private key to be placed on a cryptographic
token or smartcard, which makes it physically impossible for any
software running on the client machine (even at root/Administrator
level) to directly read the key.

UI Considerations
-----------------

Supporting External PKI profiles in the client UI should be fairly
straightforward, since an external PKI profile is very similar to
a server-locked profile.  Here is a sample profile as described
by EnumProfiles:

  {
    "access_allowed": true, 
    "authority": false, 
    "external_pki": true, 
    "global": false, 
    "host": "dfly.yonan.net:443", 
    "id": "dfly_yonan_net_externalpki_p4133", 
    "name": "dfly.yonan.net/ExternalPKI", 
    "org": "Acme Terraforming", 
    "owner": "jamesyonan", 
    "remote_hosts": [
      "dfly.yonan.net"
    ], 
    "type": [
      "dynamic"
    ]
  }

There are, however, some notable differences between external-pki
profiles and server-locked profiles:

(1) external-pki profiles can potentially be autologin, however the
    EnumProfiles description of the profile will not show an
    autologin attribute, because the autologin vs. userlogin
    determination is made by the external client certificate, not
    by the profile, and therefore is not known until connect time.
    The UI should try not to care whether or not a profile is
    autologin, and just respond appropriately to password queries
    if and when they are made by the client backed.

(2) server-locked profiles are stubs used to dynamically download
    a user-locked profile from the server.  external-pki profiles
    are already complete profiles (no user-locked profile download
    from the server is required), except the client certificate/key is
    omitted from the profile, and is accessed at connect time
    via the host OS certificate/key store.

Initial Configuration
---------------------

First, stop the Access Server:

  /etc/init.d/openvpnas stop

Configure the Access Server for External PKI usage
by editing /usr/local/openvpn_as/etc/as.conf and
commenting out the certs_db setting:

  # certificates database
  #certs_db=sqlite:///~/db/certs.db

No certificate DB will be used while the Access Server
is configured for External PKI usage.

Testing Configuration
---------------------

It is possible to use the "certool" script provided with the
Access Server to generate a simple external PKI for testing
purposes.

First, follow the "Initial Configuration" steps above.  Continue
with the Access Server daemon turned off.

Initialize the external PKI files in the epki directory,
including the CA cert/key:

  cd /usr/local/openvpn_as/scripts
  mkdir -p epki/root

First make a root cert for signing intermediate CAs.

  ./certool --dir epki/root --cn "EPKI Root Test" --type ca

Make the intermediate CA:

  ./certool --dir epki/root --type intermediate --serial 1 --cn "EPKI Intermediate" --name inter
  cp epki/root/inter.crt epki/ca.crt
  cp epki/root/inter.key epki/ca.key
  cat epki/root/ca.crt epki/ca.crt >epki/cabundle.crt

Make a certificate/key pair for the OpenVPN server:

  ./certool --dir epki --type server --serial 1 --cn server

Make a tls_auth key for the OpenVPN server:

  ./certool --dir epki --tls_auth

Generate Diffie Hellman parameters for the OpenVPN server:

  openssl dhparam -out epki/dh.pem 1024

Load the files we just generated into the Access Server
config database:

  ./confdba -mk external_pki.ta_key --value_file epki/ta.key
  ./confdba -mk external_pki.ca_crt --value_file epki/cabundle.crt
  ./confdba -mk external_pki.server_crt --value_file epki/server.crt
  ./confdba -mk external_pki.server_key --value_file epki/server.key
  ./confdba -mk external_pki.dh_pem --value_file epki/dh.pem

Configure remote certificate usage to netscape ("ns").
  ./confdba -mk external_pki.remote_cert_usage -v ns

Configure use of the X509 "role" attribute for declaration of autologin permission:

  ./confdba -mk external_pki.autologin_x509_spec -v "role,,AUTOLOGIN"

Restart the Access server:
  /etc/init.d/openvpnas start

Now we will generate a test client under the user "etest".
Depending on which authentication model you have configured
in the Access Server, add a user/password for "etest".

For example, if you are using PAM authentication:

  useradd -s /sbin/nologin etest
  passwd etest

Generate a test client cert/key pair for user "etest".  The key
will be encrypted with a password that you will be prompted for.
certool will generate the file epki/etest.p12 which contains
the cert/key pair. 

  ./certool --dir epki --type client --serial 2 --cn etest --cabundle epki/cabundle.crt --pkcs12 --prompt

If you would like to test autologin support as well, generate an autologin
cert/key pair for etest.  The generated cert/key will be in the file
epki/etestauto.p12

  ./certool --dir epki --type client --serial 3 --cn etest --name etestauto --cabundle epki/cabundle.crt --pkcs12 --prompt role=AUTOLOGIN
  ./sacli --user etest --key prop_autologin --value true UserPropPut

Finally, generate a server-locked profile.  The profile will be stored
in client.ovpn:

  ./sacli --dest_dir . GetGeneric

This completes the server side setup.  Now copy these three files to the client
machine:

  epki/etest.p12           -> the userlogin cert/key pair for user etest
  epki/etestauto.p12       -> the autologin cert/key pair for user etest
  client.ovpn              -> the server-locked profile for the Access Server

In a production setting, the client cert/key pairs (the .p12 files) would be
distributed to clients via the foreign PKI software in use, while the
client.ovpn could be distributed in the same manner as existing
server-locked profiles, such as:

  (1) transparently via the OpenVPN Connect client
  (2) Using a client software push capability.  For example, on Mac OS X, a
      client installer DMG can be generated which is pre-configured with the
      the server-locked profile:

      ./sacli --itype dmg -o . GetGenericInstaller

On Mac OS X clients, start up the Keychain Access application.
Go to File / Import Items and import one of the .p12 files you generated
above.  To decrypt the .p12 file, you will need to enter the password
you used when you generated the file.

On Windows clients, run Internet Explorer and go to Tools / Internet options.
Click on the Content tab then the Certificates button.  Click "Import" to
import a .p12 file.

Now we are ready to import the server-locked profile and connect:

  capi -f client.ovpn importprofile
  capi -D -i <PROFILE_ID> -u etest -p <PASSWORD> Connect

Whether or not the profile is an autologin profile will depend on
whether you imported epki/etest.p12 or epki/etestauto.p12
into the system certificate store.  If it is an autologin profile,
then of course the -u and -p parameters above can be omitted.

You will note that during the connection process, the system certificate
store may raise a confirmation dialog box when OpenVPN uses the
private key for an RSA sign operation.  On some Windows systems, the
confirmation dialog may start in the minimized state rather than
popping up (the confirmation dialog comes from the system certificate
store itself, so OpenVPN doesn't have any control over how it is
rendered).

Production Configuration
------------------------

The production configuration assumes the use of a third-party tool
for PKI management and certificate/key generation.

First, follow the "Initial Configuration" steps above.

Next, make a tls_auth key for the OpenVPN server and load it into
the Access Server configuration:

  cd /usr/local/openvpn_as/scripts
  mkdir epki
  ./certool --dir epki --tls_auth
  ./confdba -mk external_pki.ta_key --value_file epki/ta.key

Generate Diffie Hellman parameters for the OpenVPN server and
load them into the Access Server configuration:

  openssl dhparam -out epki/dh.pem 1024
  ./confdba -mk external_pki.dh_pem --value_file epki/dh.pem

Using the PKI management tool, generate a certificate/key pair for
the OpenVPN server.  Then import the CA certificate, server certificate,
and server private key (all specified in PEM format) into the Access Server:

  ./confdba -mk external_pki.ca_crt --value_file <CA_CERT_BUNDLE>
  ./confdba -mk external_pki.server_crt --value_file <SERVER_CERT_FILE>
  ./confdba -mk external_pki.server_key --value_file <SERVER_PRIVATE_KEY_FILE>

To avoid a particular type of man-in-the-middle attack where a client
certificate could be used to impersonate the server, make sure to
designate all certificates generated by your PKI management tool as
being explicitly client or server.  There are two ways to do this:

(1) Netscape certificate type (a netscape de-facto standard that is
    well-supported, but shunned by purists).
    Configure as follows:
      ./confdba -mk external_pki.remote_cert_usage -v ns

(2) X509 explicit/extended key usage based on RFC3280 TLS rules.
    Configure as follows:
      ./confdba -mk external_pki.remote_cert_usage -v eku


Client Support
--------------

When used in External PKI mode, the Connect Client supports
the Mac Keychain or Windows certificate store as a source
from which to fetch the client certificate.

When the user attempts to connect using the profile, the client
backend will enumerate the user's host OS certificate store and
automatically select the certificate/key pair that was issued by
the OpenVPN Access Server that generated the profile.  If more
than one certificate/key pair is elgible, the certificate with
the latest expiration date will be used.

Certificate naming conventions
------------------------------

Currently, the Access Server uses the following client certificate
naming conventions:

(1) The Common Name of the certificate must match the user name
    of the connecting client.

(2) Autologin certificates, which don't require username/password
    authentication (only client certificate authentication), must
    be tagged in some way to indicate that they hold this right.
    In the above example, we set the X509 role attribute to
    AUTOLOGIN to specify an autologin right.  Then we modify the
    access server configuration to indicate that autologin is
    enabled if the X509 "role" attribute contains the "AUTOLOGIN"
    substring.

    ./confdba -mk external_pki.autologin_x509_spec -v "role,,AUTOLOGIN"

    If external_pki.autologin_x509_spec is omitted, all autologin
    access is disabled.

    The external_pki.autologin_x509_spec string is formatted as
    follows:

      <X509 Attribute>,<optional flags>,<enabling substring in attribute value>

    The flags parameter is reserved for future use and is
    currently unimplemented.

    So, for example, if external_pki.autologin_x509_spec is set to
    "role,,AUTOLOGIN", as the confdba command above does, then the
    Access Server would only allow autologin connections from
    client certificates where the "role" X509 attribute is present
    and the substring "AUTOLOGIN" exists within the "role" value.
  
Advanced features
-----------------

Split CA, i.e. using a separate CA chain for client and server certificates:

  By default, one CA chain is assumed for both client and server
  certificates.  To use a separate chain for each, first enable
  split CA mode:

  ./confdba -mk external_pki.remote_cert_usage -v split

  Next, replace the following line (from above):

  ./confdba -mk external_pki.ca_crt --value_file <CA_CERT_BUNDLE>

  with:

  ./confdba -mk external_pki.server_ca_crt --value_file <SERVER_CA_CERT_BUNDLE>
  ./confdba -mk external_pki.client_ca_crt --value_file <CLIENT_CA_CERT_BUNDLE>

  When using split CA mode, marking certificates as client or server
  becomes unnecessary.  Therefore, settings for Netscape certificate
  type or X509 explicit/extended key usage based on RFC3280 TLS rules
  can be omitted.

  When running a standalone OpenVPN client with a split-CA configuration,
  you must use a newer version of OpenVPN that supports the "extra-certs"
  directive.  Use at least version 2.1.3w available from:

  http://svn.openvpn.net/projects/openvpn/branches/2.1/openvpn

Controlling CN/username requirement:

  By default, it is required that client logins use a username that
  matches the Common Name on the client certificate.  This requirement
  may be dropped by disabling the external_pki.cn_username_requirement
  boolean key:

  ./sacli -k external_pki.cn_username_requirement -v false ConfigPut

Controlling which certificate store the client uses to fetch the
client certificate:

  By default, the client will fetch certificates from the "user" store.
  This corresponds to the user's ~/Library/Keychains/login.keychain
  store on Mac, and the the "MY" store on Windows.

  On Mac, the store can be selected on the server using the
  user properties key "cert_store".  Set it to "user", "system", or
  "both" to explicitly control the store that clients use.

  cli_cert_store:
    "user"   : use ~/Library/Keychains/login.keychain
    "system" : use /Library/Keychains/System.keychain
    "both"   : try system keychain, then fall back to user
  
  For example, set the store to "both":

  ./sacli --user __DEFAULT__ --key cli_cert_store --value "both" UserPropPut
