FortiGate IPsec Site-to-Site VPN to Cisco ASA on FortiOS 7.4

FortiGate IPsec Site-to-Site VPN to Cisco ASA on FortiOS 7.4

FortiGate IPsec Site-to-Site VPN to Cisco ASA on FortiOS 7.4

The Problem

Building an IPsec tunnel between a FortiGate and a Cisco ASA is one of those tasks that looks straightforward until Phase 2 refuses to come up and you’re staring at two different vendor debug outputs trying to figure out which side is lying. The encryption parameters, PFS settings, and traffic selectors all have to match exactly — and each platform expresses them slightly differently.

This post walks through a production setup I built between a branch FortiGate 60F running FortiOS 7.4.4 and an HQ Cisco ASA 5516-X running ASA 9.16(4). Both sides have static public IPs. The tunnel uses IKEv2 with a pre-shared key, AES-256/SHA-256 encryption, and DH group 14. I’ll cover both sides of the config, the CLI commands to verify status, and the exact errors I ran into during the build.


Environment Summary

Parameter Value
Branch device FortiGate 60F, FortiOS 7.4.4
HQ device Cisco ASA 5516-X, ASA 9.16(4)
FortiGate public IP 203.0.113.5
ASA public IP 198.51.100.10
Branch subnet 10.20.0.0/24
HQ subnet 10.10.0.0/24
IKE version IKEv2
Authentication Pre-shared key
Encryption AES-256
Integrity SHA-256
DH group 14
Phase 1 lifetime 28800 seconds
Phase 2 lifetime 3600 seconds
PFS Enabled, DH group 14

Step 1 — Configure the FortiGate Phase 1 (IPsec Wizard)

Go to VPN > IPsec Wizard, select Custom, and name the tunnel vpn-hq-asa.

Fill in the Phase 1 parameters as follows:

Field Value
Remote Gateway Static IP Address
IP Address 198.51.100.10
Interface wan1
IKE Version 2
Authentication Method Pre-shared Key
PSK (your shared secret)
Encryption AES256
Authentication SHA256
Diffie-Hellman Group 14
Lifetime 28800

Leave Dead Peer Detection enabled with the default on-demand setting. The FortiGate wizard does not always expose every advanced IKEv2 parameter in the GUI, so after finishing the wizard, verify the Phase 1 config from CLI:

show vpn ipsec phase1-interface vpn-hq-asa

You should see output that includes:

config vpn ipsec phase1-interface
    edit "vpn-hq-asa"
        set interface "wan1"
        set ike-version 2
        set keylife 28800
        set peertype any
        set proposal aes256-sha256
        set dhgrp 14
        set remote-gw 198.51.100.10
        set psksecret ENC <encrypted-secret>
    next
end

If ike-version shows 1, the wizard silently reverted it. Set it manually:

config vpn ipsec phase1-interface
    edit "vpn-hq-asa"
        set ike-version 2
    next
end

Step 2 — Configure the FortiGate Phase 2

Still in the wizard (or via VPN > IPsec Tunnels > Edit > Phase 2 Selectors), configure:

Field Value
Local Address 10.20.0.0/24
Remote Address 10.10.0.0/24
Encryption AES256
Authentication SHA256
Enable PFS Yes
DH Group 14
Keylifetime 3600 seconds

Verify from CLI:

show vpn ipsec phase2-interface

Expected output:

config vpn ipsec phase2-interface
    edit "vpn-hq-asa-p2"
        set phase1name "vpn-hq-asa"
        set proposal aes256-sha256
        set dhgrp 14
        set pfs enable
        set keylifeseconds 3600
        set src-subnet 10.20.0.0 255.255.255.0
        set dst-subnet 10.10.0.0 255.255.255.0
    next
end

Pay attention to pfs enable and dhgrp 14. These two fields must align with whatever the ASA is configured to negotiate. If one side has PFS enabled and the other does not, Phase 2 will not come up and you’ll see no SA proposal chosen in the FortiGate debug.


Step 3 — Add a Static Route

The tunnel interface needs a route pointing traffic toward it. Go to Network > Static Routes > Create New:

Field Value
Destination 10.10.0.0/24
Interface vpn-hq-asa
Distance 1

From CLI:

config router static
    edit 0
        set dst 10.10.0.0 255.255.255.0
        set device "vpn-hq-asa"
        set distance 1
    next
end

Verify it shows up in the routing table:

get router info routing-table static

You should see:

S       10.10.0.0/24 [1/0] via vpn-hq-asa tunnel 198.51.100.10

Step 4 — Create Firewall Policies

You need two policies: one for outbound traffic (LAN to VPN) and one for inbound (VPN to LAN). NAT must be disabled on both.

Policy 1 — LAN to HQ (outbound):

config firewall policy
    edit 0
        set name "LAN-to-HQ-VPN"
        set srcintf "internal"
        set dstintf "vpn-hq-asa"
        set srcaddr "10.20.0.0/24"
        set dstaddr "10.10.0.0/24"
        set action accept
        set schedule "always"
        set service "ALL"
        set nat disable
    next
end

Policy 2 — HQ to LAN (inbound):

config firewall policy
    edit 0
        set name "HQ-VPN-to-LAN"
        set srcintf "vpn-hq-asa"
        set dstintf "internal"
        set srcaddr "10.10.0.0/24"
        set dstaddr "10.20.0.0/24"
        set action accept
        set schedule "always"
        set service "ALL"
        set nat disable
    next
end

NAT off is critical. If NAT is enabled on the outbound policy, the FortiGate will SNAT traffic to its WAN IP before encapsulating it, which breaks the Phase 2 traffic selector match.


Step 5 — Configure the Cisco ASA

Here is the complete ASA configuration for this tunnel. The ASA initiates as a responder here, but these commands also work if the ASA is the initiator.

IKEv2 Policy:

crypto ikev2 policy 10
 encryption aes-256
 integrity sha256
 group 14
 prf sha256
 lifetime seconds 28800

IPsec Proposal:

crypto ipsec ikev2 ipsec-proposal AES256-SHA256
 protocol esp encryption aes-256
 protocol esp integrity sha-256

Access List defining interesting traffic (encryption domain):

access-list VPN-ACL extended permit ip 10.10.0.0 255.255.255.0 10.20.0.0 255.255.255.0

Crypto Map:

crypto map vpn-map 10 match address VPN-ACL
crypto map vpn-map 10 set peer 203.0.113.5
crypto map vpn-map 10 set ikev2 ipsec-proposal AES256-SHA256
crypto map vpn-map 10 set pfs group14
crypto map vpn-map interface outside

Tunnel Group:

tunnel-group 203.0.113.5 type ipsec-l2l
tunnel-group 203.0.113.5 ipsec-attributes
 ikev2 remote-authentication pre-shared-key <PSK>
 ikev2 local-authentication pre-shared-key <PSK>

Note that IKEv2 on the ASA requires both remote-authentication and local-authentication pre-shared keys set explicitly under the tunnel-group. IKEv1 only required one. Missing either one will cause the Phase 1 authentication exchange to fail.

Enable IKEv2 on the outside interface if not already:

crypto ikev2 enable outside

Step 6 — Bring Up the Tunnel and Verify

On the FortiGate, bring up the tunnel by sending interesting traffic or using the GUI toggle. Then verify:

diagnose vpn ike gateway list name vpn-hq-asa

Look for IKEv2 established in the output. A healthy Phase 1 shows:

vd: root/0
name: vpn-hq-asa
version: 2
interface: wan1 6
addr: 203.0.113.5:500 -> 198.51.100.10:500
created: Xs ago
IKE SA: created 1/1  established 1/1  time 0/0/0 ms
IPsec SA: created 1/1  established 1/1  time 0/0/0 ms

Verify Phase 2 and see packet counters:

diagnose vpn tunnel list name vpn-hq-asa

You should see enc and dec counters incrementing if traffic is passing.

get router info routing-table static

Confirm the static route to 10.10.0.0/24 is present.

On the ASA, verify with:

show crypto ikev2 sa
show crypto ipsec sa peer 203.0.113.5

For show crypto ipsec sa, look at the #pkts encaps and #pkts decaps counters. If encaps is incrementing but decaps is not, traffic is leaving the ASA but not returning — check the FortiGate firewall policy for the inbound direction.

To simulate traffic without a live host:

packet-tracer input inside icmp 10.10.0.5 8 0 10.20.0.5 detailed

This traces an ICMP packet from an HQ host toward the branch and shows every policy/crypto decision the ASA makes.


Troubleshooting

Error 1: Phase 1 Comes Up but Phase 2 Stays Down

Symptom: diagnose vpn ike gateway list shows Phase 1 established, but diagnose vpn tunnel list shows no SA and no packet counters.

Most likely cause: Mismatched traffic selectors (encryption domain). The FortiGate quick mode selector and the ASA crypto map ACL must define the exact same source and destination subnets. If the ASA ACL matches 10.10.0.0/25 and the FortiGate selector is 10.10.0.0/24, the negotiation fails.

Fix: Align both sides to the same exact subnet. On the FortiGate, check:

show vpn ipsec phase2-interface

On the ASA:

show access-list VPN-ACL

Make sure the subnet masks match exactly on both sides.


Error 2: “no SA proposal chosen”

Symptom: FortiGate debug (diagnose debug application ike -1) shows no SA proposal chosen during Phase 2 negotiation.

Cause: PFS mismatch. One side has PFS enabled with DH group 14, the other has PFS disabled or uses a different group.

Fix: On the FortiGate, confirm:

show vpn ipsec phase2-interface

Confirm set pfs enable and set dhgrp 14. On the ASA, confirm the crypto map includes:

crypto map vpn-map 10 set pfs group14

If you want to disable PFS, remove it from both sides. Either both enabled with the same group, or both disabled — no mixing.


Error 3: Phase 1 Fails — Authentication Error

Symptom: diagnose vpn ike gateway list shows no established SA, and IKE debug shows authentication failed.

Cause 1: PSK mismatch. Copy-paste errors are common here, especially with special characters.

Cause 2: On IKEv2, the ASA requires both ikev2 remote-authentication pre-shared-key and ikev2 local-authentication pre-shared-key under the tunnel-group. If only one is set, authentication fails.

Fix: Verify both keys on the ASA:

show running-config tunnel-group 203.0.113.5

Both lines must be present. Reset the PSK on both sides to a known value and retest.


Error 4: Tunnel Is Up but No Traffic Passes

Symptom: Both sides show established SAs with packet counters, but pings fail end-to-end.

Cause: NAT is enabled on the FortiGate firewall policy, or a firewall policy in one direction is missing.

Fix: Check both firewall policies exist and NAT is off:

show firewall policy

Look for nat: disable on both VPN-related policies. If NAT is enabled, traffic gets SNAT’d before matching the Phase 2 selector, and the ASA drops it because the source IP no longer matches the ACL.


Error 5: Tunnel Drops Periodically

Symptom: Tunnel comes up fine but drops every few hours and re-establishes.

Cause: Phase 1 or Phase 2 lifetime mismatch causing a race condition during rekey, or DPD is triggering on an idle tunnel.

Fix: Align lifetimes exactly on both sides (Phase 1: 28800, Phase 2: 3600 as configured here). If the tunnel carries low-volume traffic, also check that DPD isn’t timing out during quiet periods. On FortiGate:

config vpn ipsec phase1-interface
    edit "vpn-hq-asa"
        set dpd on-demand
        set dpd-retrycount 3
        set dpd-retryinterval 20
    next
end

on-demand only sends DPD probes when there is traffic to send, which avoids false failures on low-traffic tunnels.


FAQ

How do I troubleshoot Phase 2 failure on FortiGate IPsec VPN?

Start with the IKE debug on the FortiGate:

diagnose debug reset
diagnose debug application ike -1
diagnose debug enable

Trigger the tunnel by sending traffic or toggling the interface down/up. Watch for the Phase 2 exchange — look for no SA proposal chosen (proposal mismatch), TS_UNACCEPTABLE (traffic selector mismatch), or authentication failed (PSK issue). Turn off debug when done:

diagnose debug disable
diagnose debug reset

Also run diagnose vpn tunnel list name vpn-hq-asa to check packet counters. Zero dec with non-zero enc means traffic is leaving but not returning — look at the far-end firewall policy or routing.


What encryption domain should I use for FortiGate to Cisco ASA?

The encryption domain is the set of source and destination subnets that define what traffic is protected by the tunnel. On the FortiGate, this is the Phase 2 local and remote subnet selectors. On the ASA, this is the ACL referenced by the crypto map.

Both sides must match exactly — same network address, same subnet mask. Do not summarize one side. If the FortiGate protects 10.20.0.0/24 and the ASA ACL says 10.20.0.0/25, Phase 2 will not establish. The platforms compare these selectors during the IKEv2 TS (Traffic Selector) negotiation and reject mismatches.

For this setup: FortiGate local 10.20.0.0/24, remote 10.10.0.0/24. ASA ACL: permit ip 10.10.0.0/24 to 10.20.0.0/24. Exact match, no summarization.


How do I match PFS settings between FortiGate and ASA?

PFS must be either enabled with the same DH group on both sides, or disabled on both sides. There is no middle ground.

FortiGate (PFS enabled, DH 14):

set pfs enable
set dhgrp 14

ASA (PFS enabled, DH 14):

crypto map vpn-map 10 set pfs group14

To disable PFS on both sides:

FortiGate:

set pfs disable

ASA: remove the set pfs line from the crypto map entry.

If you toggle PFS on the FortiGate and forget to update the ASA (or vice versa), you’ll get no SA proposal chosen every time Phase 2 tries to negotiate.


What CLI commands verify IPsec status on FortiGate?

These are the four commands I use in order:

1. Check Phase 1 (IKE SA) status:

diagnose vpn ike gateway list name vpn-hq-asa

2. Check Phase 2 (IPsec SA) status and packet counters:

diagnose vpn tunnel list name vpn-hq-asa

3. Confirm routing is in place:

get router info routing-table static

4. Run live IKE debug during negotiation:

diagnose debug application ike -1
diagnose debug enable

For a broader view of all active tunnels, drop the name filter:

diagnose vpn ike gateway list
diagnose vpn tunnel list

Conclusion

Building a FortiGate-to-ASA IKEv2 tunnel is a straightforward process once you understand where the two platforms use different terminology for the same concepts. The encryption domain (FortiGate’s Phase 2 selectors vs. the ASA’s crypto map ACL) and PFS configuration are the two most common sources of Phase 2 failures — get those aligned and the rest usually falls into place.

Key takeaways from this build:

  • Match encryption domain subnets exactly. No summarization on one side.
  • PFS must be consistently enabled or disabled with the same DH group on both platforms.
  • IKEv2 on the ASA requires both remote-authentication and local-authentication PSK entries under the tunnel-group.
  • Use diagnose vpn tunnel list on the FortiGate to read packet counters — it’s the quickest way to confirm if encrypted traffic is actually flowing.

Once the tunnel is up, the day-two work is verification — when something drops at 3 AM you want the CLI flow already in muscle memory. The diagnostic companion to this post is Cómo verificar un túnel IPsec en FortiGate por CLI (FortiOS 7.4.4) (Spanish), which walks the diagnose vpn ike gateway listdiagnose vpn tunnel list → IKE debug → sniffer flow against the same FortiGate ↔ ASA setup built here. If the bigger picture is that you’re moving off SSL VPN and onto IPsec across the board — not just site-to-site but also dialup — the pre-migration audit lives in the FortiGate SSL VPN Migration Checklist (FortiOS 7.6). Both posts plus the rest of the FortiGate work on this site are organized in the FortiGate Field Guide.

Share Twitter LinkedIn

Need help with this in production?

I take on FortiGate, Cisco, and Microsoft work end-to-end — multi-vendor migrations, troubleshooting that needs a second pair of eyes, and engagements that scope bigger than they first looked.

Discuss your scenario →