> For the complete documentation index, see [llms.txt](https://docs.stoik.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.stoik.io/cyber-best-practices/harden-your-environment/implement-certificates/certificate-based-mfa-on-fortigate.md).

# Certificate-based MFA on Fortigate

## Goal

Require a valid user certificate for SSL-VPN users in a specific AD group.

## Prerequisites

* Have already deployed certificates to your users via ADCS. If not, follow [Deploying user certificates within Active Directory](broken://pages/8dba81d5484d2cb53fefcb8f1832c7a90387ec09).
* Have configured LDAP authentication based on your Active Directory.

{% hint style="info" %}
If you manage FortiGate via FortiManager, steps and locations can differ. Ask an engineer for the FortiManager flow.
{% endhint %}

{% stepper %}
{% step %}

#### Export your ADCS root CA certificate (public key only)

Export the **public** certificate of your ADCS root CA from any domain workstation.

Find it in **Certificates - Current User → Trusted Root Certification Authorities**.

Export it as a `.cer` / Base-64 certificate (no private key).

![](https://content.gitbook.com/content/jlP9dfgnu6hj9gyZJG1g/blobs/UoNOEDpWuW7doKmRUzMQ/image%20png%20Apr%2016%202024%2010%2029%2033%206039%20AM.png)
{% endstep %}

{% step %}

#### Import the root CA into FortiGate

Import your CA root certificate in **System → Certificates → Create/Import → CA Certificate**.

![](https://content.gitbook.com/content/jlP9dfgnu6hj9gyZJG1g/blobs/V3MxpV96ygH68ZpRDRwq/image%20png%20Apr%2016%202024%2010%2025%2056%200473%20AM.png)

![](https://content.gitbook.com/content/jlP9dfgnu6hj9gyZJG1g/blobs/Xk7KC0PNyCxiGQ4AdrHd/image%20png%20Apr%2016%202024%2010%2030%2006%206702%20AM.png)

For clarity, we recommend renaming the imported certificate (by default named Ca\_Cert\_X where X is a number) to a more meaningful name. To do so, open the FortiGate command line interface and enter the following commands:

```shell
config vpn certificate ca
rename Ca_Cert_X to ADCS_Cert
```

The certificate should then appear under its new name.

![](https://content.gitbook.com/content/jlP9dfgnu6hj9gyZJG1g/blobs/G7n13fPuW6Idbyq1rWpC/image%20png%20Apr%2016%202024%2005%2015%2000%207849%20PM.png)
{% endstep %}

{% step %}

#### Create a user peer (PKI) that ties the cert to LDAP

You’ll reference this user peer from your SSL-VPN authentication rule.

Replace:

* `ADCS_Cert` with your imported CA name (the rename above).
* `<LDAP_SERVER_NAME>` with the FortiGate LDAP server object name from **User & Authentication → LDAP Servers**.

{% tabs %}
{% tab title="FortiOS < 7.x" %}

```shell
config user peer
    edit pki01
        set ca ADCS_Cert
        set ldap-server <LDAP_SERVER_NAME>
        set ldap-mode principal-name
    next
end
```

{% endtab %}

{% tab title="FortiOS 7.x+" %}

```shell
config user peer
    edit pki01
        set ca ADCS_Cert
        set mfa-mode subject-identity
        set mfa-server <LDAP_SERVER_NAME>
    next
end
```

{% endtab %}
{% endtabs %}
{% endstep %}

{% step %}

#### Create an AD group for certificate users

To facilitate MFA deployment among your employees, we recommend creating a new Active Directory group. This will allow you to gradually migrate users from the "VPN without certificate" group to the "VPN with certificate" group. Create this new group in your Active Directory domain using the **"Active Directory Users and Computers"** tool. In the example below, we named it **"NOMADE\_CERT"**:

![](https://content.gitbook.com/content/jlP9dfgnu6hj9gyZJG1g/blobs/TuGxlrAVtRfI25kfwPym/image%20png%20Apr%2016%202024%2005%2021%2048%206625%20PM.png)

![](https://content.gitbook.com/content/jlP9dfgnu6hj9gyZJG1g/blobs/eheHigBZOrFkD0xLQfiy/image%20png%20Apr%2016%202024%2005%2022%2042%205366%20PM.png)
{% endstep %}

{% step %}

#### Create the FortiGate user group mapped to the AD group

Create a new **User Group** under **User & Authentication → User Groups**.

Set:

* **Remote Server** = your LDAP object
* **Group Name** = `NOMADE_CERT` (or your chosen AD group)

![](https://content.gitbook.com/content/jlP9dfgnu6hj9gyZJG1g/blobs/LNBcvH9j1RgvYGngilpZ/image%20png%20Apr%2016%202024%2005%2026%2011%202110%20PM.png)

![](https://content.gitbook.com/content/jlP9dfgnu6hj9gyZJG1g/blobs/To3fBfEgDMYVRasWUnu0/image%20png%20Apr%2016%202024%2005%2027%2030%203309%20PM.png)
{% endstep %}

{% step %}

#### Map the user group to your SSL-VPN portal

In **SSL-VPN Settings**, map the new group to the portal you want.

![](https://content.gitbook.com/content/jlP9dfgnu6hj9gyZJG1g/blobs/XSSm8fI5AwryXzW5cDtw/image%20png%20Apr%2016%202024%2005%2029%2031%209722%20PM.png)

Open the FortiGate CLI again and enter the following command:

```shell
conf vpn ssl settings
conf authentication-rule
show
```

The list of existing mappings should then be displayed, as in the example below:

![](https://content.gitbook.com/content/jlP9dfgnu6hj9gyZJG1g/blobs/gGliR4mrzptssu5yE9ls/image%20png%20Apr%2016%202024%2005%2032%2041%205333%20PM.png)

You must now identify the newly created mapping. In this example, it is number 4, associated with the group `NOMADE_CERT`.

Enter the following commands (replace `4` with your mapping ID):

```shell
edit 4
set client-cert enable
set user-peer pki01
end
show
```

You should then see the updated list of mappings:

![](https://content.gitbook.com/content/jlP9dfgnu6hj9gyZJG1g/blobs/kUeeAKv0OYZCKkN7tYgj/image%20png%20Apr%2016%202024%2005%2036%2040%204046%20PM.png)

Users in the AD domain who belong to `NOMADE_CERT` must now present a valid user certificate to connect.
{% endstep %}

{% step %}

#### Test from FortiClient

On a domain-joined workstation, update the FortiClient VPN profile:

![](https://content.gitbook.com/content/jlP9dfgnu6hj9gyZJG1g/blobs/rPmNtIfwmzAW9DTAwqjc/image%20png%20Apr%2016%202024%2005%2038%2049%208834%20PM.png)

Select the certificate associated with your user (here "stoik\_test") in the **"Client Certificate"** field:

![](https://content.gitbook.com/content/jlP9dfgnu6hj9gyZJG1g/blobs/eU1qkoZ7RUk6smk1PCYR/image%20png%20Apr%2016%202024%2005%2046%2026%207990%20PM.png)
{% endstep %}
{% endstepper %}

## Troubleshooting authentication issues

The following commands will allow you to view debug logs for LDAP and authentication processes in general (the commands must be entered in the FortiGate CLI, and the session must remain open):

```shell
diagnose debug enable
diagnose debug application fnbamd -1
diagnose debug application authd -1
diagnose debug application ldap -1
```

If the above commands do not help identify the issue (incorrect information in the certificate, LDAP server unreachable, etc.), it may be useful to enable SSL VPN debug logging:

```shell
diagnose debug app sslvpn -1
diagnose vpn ssl debug-filter src-addr4 XXX.XXX.XXX.XXX
```

To limit the amount of displayed logs, replace XXX.XXX.XXX.XXX above with the source IP address initiating the VPN connection.

Once debugging is complete, you must disable debug mode:

```shell
diagnose debug disable
diagnose debug reset
```

Please note that this procedure can be somewhat complex, as it requires being simultaneously connected to the FortiGate and able to launch FortiClient. Do not hesitate to contact our experts if necessary.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.stoik.io/cyber-best-practices/harden-your-environment/implement-certificates/certificate-based-mfa-on-fortigate.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
