In this recipe, you will configure an SSL VPN tunnel that requires users to authenticate solely with a certificate. We will configure a PKI peer object in order to search our LDAP using the certificate’s UserPrincipalName in order to determine group memberships of the user. We will then be able to base our SSL VPN policies on LDAP group membership, without the need to explicitly request the user’s LDAP credentials during the client VPN session establishment.
This recipe was tested with a Windows 2012R2 Active Directory acting as both the user certificate issuer, the certificate authority and the LDAP server.
While it is possible to force explicit LDAP authentication for the user during VPN establishment, this cookbook article’s goal is to offer a true “single-sign on” approach in which we use pre-established credentials (the user’s issued certificate) while maintaining the ability to know what Active Directory groups the connecting user belongs to based on the username found in the certificate.
Note that this article skims over the basics of SSL VPN configuration – refer to other more basic articles if you need a refresher on the basic mechanics related to SSL VPN or other features found in this article.
While it is not the goal of this article to cover Microsoft’s Certificate Authority and its operation, suffice to note that our test user (user1) has been issued a standard “User” template certificate for which he has enrolment permissions for.
In our case, we duplicated the standard “User” template and made certain that we have the User Principal Name included in the subject name of the issued certificate – this is the user field we will be using to search LDAP during the connection attempt.
For reference, UPN designates the format “user@domain”, with domain being the Active Directory domain.
Using our Microsoft Certificate Authority’s web interface, we request a “Web Server” type certificate and use the CSR generated from FortiOS’s GUI to obtain a trusted certificate. Do note that “trusted” in this example is limited in scope to the organization’s assets – you would use a public CA to generate a certificate for FortiOS if you expect non-corporate assets to connect to the SSL VPN as those are unlikely to trust the Active Directory CA we are using in our example.
While the below process goes through the manual certificate request process, FortiOS is SCEP capable which can be used to automate the certificate request process with a SCEP-compliant CA server (Microsoft CA does support SCEP).
Export the CA certificate from your CA using the available methods. In our case, we use the Windows CA web interface to download our CA cert in BASE64 format.
Go to System > Certificates and select Import > CA Certificate.
Select the Microsoft CA certificate file.
The CA certificate now appears in the list of Certificates. Note that it is named “G_CA_Cert_1″ – keep that in mind as we refer to this later in the article.
Next, we generate a CSR on FortiOS which we will use to obtain a signed certificate from our CA, again in our case the Microsoft CA. The domain name used should match the domain name users will be connecting to using FortiClient and is generally what resolves to the IP of the interface listening for SSL VPN.
Download the generated CSR, which is a text file containing the BASE64 certificate request.
We again use our Microsoft CA web interface to submit our CSR and obtain a certificate of type “Web Server”.
Download the resulting signed request in BASE64 format.
Finally, import that signed request as a local certificate on FortiOS to finalize our SSL VPN server certificate.
Our request is complete and our certificate is now usable. We will use this certificate later in our SSL VPN configuration.
As we will be validating incoming VPN requests using the UserPrincipalName found in the trusted certificates used by clients, we need to define our LDAP server.
Go to User & Device > LDAP Servers and create a new LDAP server definition.
This definition is common, except for the fact that we will be using UserPrincipalName as our Common Name Identifier – the UPN field is what we are extracting from the certificates and need to match to locate users in LDAP.
Next, head to the CLI. This is the only part of this article that requires a CLI definition.
Create a PKI “peer” object as shown. This is a relatively static object which will not require frequent visits to the CLI.
config user peer edit "FORTIQC_CERTS" set ca "G_CA_Cert_1" set ldap-server "FORTIQC" set ldap-mode principal-name next end
A PKI “peer” object is created in order to instruct FortiOS to match an incoming certificate’s UserPrincipalName to a target LDAP server object, providing that certificate is signed by the designated CA and is valid. You will recognize the G_CA_Cert_1 as the name of the CA certificate we imported earlier.
The “ldap-mode” parameter is important as it dictates that authentication is not explicit (the user does not need to pass a username and password) and instead is based on validating that the UserPrincipalName found in the certificate does exist. We will extend this in a moment to also request that the user be a member of a specific LDAP group.
Next, we configure the group object that will assemble our previously configured LDAP and PKI objects together.
Go to User & Device > User groups and create a new group.
Add the PKI peer object previously created as a local member of the group.
Next add a remote group on the LDAP server and select the group of interest you need these users to be members of using the LDAP browser window.
What just happened here?
This configuration is counter-intuitive at first glance as matching against a group object generally means matching at least one of its members.
However when using a PKI object in the “member” field, the group object’s behaviour change and instead, the group will only match if the PKI object is true (the certificate is valid and trusted and the user exists in LDAP) AND the group memberships obtained from LDAP for the user also match one of the remote LDAP groups defined.
We will look at connection debug information later to see this process happening.
Go to VPN > SSL-VPN Settings.
Ensure that the Require Client Certificate option is checked.
Select the certificate we generated earlier for FortiOS.
If needed, map our newly created group to a specific portal definition. This is only necessary if the default portal (designated by “All Other Users/Groups” entry) is not the right one. In our case, it wasn’t necessary to define the group to portal mapping as the default portal was the same.
Finally, under Policy & Objects > IPv4 Policy create or modify your existing SSL VPN policies to incorporate your new group.
Our FortiClient is configured with the target hostname and local certificate issued to the user. Connecting to the VPN requires neither username or password – only the user’s certificate.
Lets look at the output of “diag debug app fnbamd -1” while the user connects. We have shortened the output of the diag in a few locations to focus on the important parts.
We can see the lookups being done to find the group memberships (3 groups total) of the user and that the correct group being found results in a match.
We can also use “diag firewall auth list” to validate that a firewall user entry exists for our SSLVPN user and is part of the right groups.
As a reference, fnbamd is short for “Fortinet Non-Blocking Authentication Management Daemon” and is the process responsible for the vast majority of explicit authentication duties found in FortiOS.
MN140D-1 (root) # diag debug reset diagnose debug MN140D-1 (root) # diag debug app fnbamd -1 Debug messages will be on for 30 minutes.  cert_check_group_list-checking group type 1 group name 'FORTIQC_PKI_GrpCertAuth'  quick_check_peer-Cert subject 'CN = user1'  check_add_peer-check peer user 'FORTIQC_CERTS' in group 'FORTIQC_PKI_GrpCertAuth', result is 4  cert_check_group_list-Status pending for group 'FORTIQC_PKI_GrpCertAuth'  resolve_ldap_FQDN-Resolved address 192.168.129.40, result 192.168.129.40  fnbamd_ldap_init-search filter is: (&(userPrincipalNameemail@example.com) (!(UserAccountControl:1.2.840.1135220.127.116.113:=2)))  fnbamd_ldap_init-search base is: dc=fortiqc,dc=local  start_search_dn-base:'dc=fortiqc,dc=local' filter:(&(userPrincipalNamefirstname.lastname@example.org) (!(UserAccountControl:1.2.840.113518.104.22.1683:=2))) ...  get_all_dn-Found 1 DN's  start_user_attrs_lookup-Adding attr 'memberOf'  start_user_attrs_lookup-base:'CN=user1,CN=Users,DC=fortiqc,DC=local' filter:cn=* ...  fnbamd_ldap_get_result-Going to DONE state res=0  __ldap_copy_grp_list-copied CN=GrpCertAuth,CN=Users,DC=fortiqc,DC=local  __ldap_copy_grp_list-copied CN=testgroup,CN=Users,DC=fortiqc,DC=local  __ldap_copy_grp_list-copied CN=Domain Users,CN=Users,DC=fortiqc,DC=local  __match_ldap_group-Matching server 'FORTIQC' - 'FORTIQC'  __match_ldap_group-Matching group 'CN=GrpCertAuth,CN=Users,DC=fortiqc,DC=local' - 'CN=GrpCertAuth,CN=Users,DC=fortiqc,DC=local'  fnbamd_auth_cert_poll-Result for ldap svr '192.168.129.40' is SUCCESS  fnbamd_auth_cert_poll-matched user 'FORTIQC_CERTS', matched group 'FORTIQC_PKI_GrpCertAuth' ... MN140D-1 (root) # diag firewall auth list 10.212.134.200, cn=user1, FORTIQC_PKI_GrpCertAuth type: fw, id: 0, duration: 181, idled: 0 expire: 28797, allow-idle: 28797 flag(a0): idle sslvpn packets: in 1427 out 1505, bytes: in 291364 out 359030 group_id: 2 group_name: FORTIQC_PKI_GrpCertAuth ----- 1 listed, 0 filtered ------
Finally, our logs show our LDAP user’s traffic:
This article presented a technique allowing for “credential-less” VPN connectivity using certificates while maintaining the ability to authorize access with policies that are based on LDAP groups. As a side note, this technique may not be suitable to the levels of security requirements of all environments as it forgoes explicit authentication in addition to PKI authentication. Your organization’s security versus ease-of-use requirements ultimately dictate the requirements.