Overview

In this article, I document the process of building a site-to-site IPsec VPN between a FortiGate firewall with a public IP address and an OPNsense firewall located behind NAT.

This scenario is common in real-world environments where one side of the tunnel is hosted behind an ISP router or does not have a stable public IP address. The configuration shown here works reliably by combining IKEv2, pre-shared keys, and Peer ID–based authentication.

The setup uses OPNsense 25.7.10, which is important because this version relies on the newer IPsec Connections framework rather than the legacy IPsec model.

A video walkthrough of this configuration is available on YouTube. This post serves as a written reference and summary, including the reasoning behind key design choices.


Network Topology

On the FortiGate side, the internal network is:

  • 172.16.1.0/24
  • Test host: 172.16.1.250

The FortiGate has a public-facing WAN interface. In this documentation, a DNS alias (example.com) is used instead of exposing the real public IP address. Some screenshots in the video are intentionally blurred for this reason, but all relevant configuration steps remain visible.

On the OPNsense side, the firewall is located behind an ISP NAT router:

  • Internal network: 10.10.30.0/24
  • Test host inside this network

Because OPNsense does not have a directly reachable public IP, the VPN must be initiated from this side, and peer identification cannot rely on source IP addresses alone.

Topology


FortiGate Configuration

Creating the IPsec Tunnel

Navigate to:

VPN → IPsec Tunnels → Create New

  • Tunnel name: VPN_Demo
  • Configuration type: Custom
  • Remote Gateway: Dialup User
  • Interface: WAN interface used for VPN

The Dialup User option is required because the OPNsense peer is behind NAT and does not have a fixed public IP address.


Authentication Settings

  • IKE Version: IKEv2
  • Peer Options: Specific Peer ID
  • Peer ID: cats.net
  • Authentication Method: Pre-shared Key

Use a strong pre-shared key. This same key will later be configured on the OPNsense side.


Phase 1 Proposal

To keep the configuration simple and secure, use a single proposal:

  • Encryption: AES-256
  • Authentication: SHA-256
  • Diffie-Hellman Group: 14
  • Local ID: dogs.net

Phase 2 Configuration

This phase defines which networks will be routed through the tunnel.

  • Local Network: 172.16.1.0/24
  • Remote Network: 10.10.30.0/24

Advanced settings:

  • Encryption: AES-256
  • Authentication: SHA-256
  • Diffie-Hellman Group: 14

If you want to do it in cli commands:

# --- Phase 1 (IKE) ---
config vpn ipsec phase1-interface
  edit "VPN_Demo"
    set interface "<WAN_INTERFACE>"          # e.g. "wan1"
    set ike-version 2
    set peertype any                        # dialup
    set net-device disable
    set mode aggressive                      # often used with dialup/ID
    set proposal aes256-sha256
    set dhgrp 14
    set remote-gw 0.0.0.0                    # dialup
    set psksecret "<STRONG_PSK>"
    set localid "dogs.net"
    set peerid "cats.net"
  next
end

# --- Phase 2 (IPsec/ESP) ---
config vpn ipsec phase2-interface
  edit "VPN_Demo_p2"
    set phase1name "VPN_Demo"
    set proposal aes256-sha256
    set dhgrp 14
    set src-subnet 172.16.1.0 255.255.255.0
    set dst-subnet 10.10.30.0 255.255.255.0
  next
end

Static Route

Create a static route so the FortiGate knows how to reach the remote network.

Network → Static Routes → Create New

  • Destination: 10.10.30.0/24
  • Interface: the IPsec tunnel interface

Fortigate commands:

# --- Static route to remote LAN via IPsec ---
config router static
  edit 0
    set dst 10.10.30.0 255.255.255.0
    set device "VPN_Demo"
  next
end

Firewall Policies

Create an address object for the remote network:

Firewall → Addresses

  • Name: VPN_Demo_LAN
  • Network: 10.10.30.0/24
# --- Address object for remote network ---
config firewall address
  edit "VPN_Demo_LAN"
    set subnet 10.10.30.0 255.255.255.0
  next
end

Then create two firewall rules:

Outbound Rule

  • Incoming Interface: ISP/WAN
  • Outgoing Interface: LAN
  • Source: Local LAN
  • Destination: VPN_Demo_LAN
  • Service: ALL
  • NAT: Disabled

Return Rule

  • Incoming Interface: IPsec tunnel
  • Outgoing Interface: ISP/WAN
  • Source/Destination: reversed
  • NAT: Disabled
# --- Firewall policies (NAT disabled) ---
# Policy: LAN -> VPN
config firewall policy
  edit 0
    set name "To_VPN_Demo"
    set srcintf "<LAN_INTERFACE>"            # e.g. "lan"
    set dstintf "VPN_Demo"
    set srcaddr "<LOCAL_LAN_ADDR_OBJ>"       # or "all" if you prefer
    set dstaddr "VPN_Demo_LAN"
    set action accept
    set schedule "always"
    set service "ALL"
    set nat disable
  next
end

# Policy: VPN -> LAN (return traffic)
config firewall policy
  edit 0
    set name "From_VPN_Demo"
    set srcintf "VPN_Demo"
    set dstintf "<LAN_INTERFACE>"
    set srcaddr "VPN_Demo_LAN"
    set dstaddr "<LOCAL_LAN_ADDR_OBJ>"       # or "all"
    set action accept
    set schedule "always"
    set service "ALL"
    set nat disable
  next
end

At this point, the FortiGate configuration is complete.


OPNsense Configuration

Before configuring OPNsense, verify that connectivity is currently failing. This confirms that traffic is not leaking and that the VPN is not already active.


Pre-Shared Key Configuration

Navigate to: VPN → IPsec → Pre-Shared Keys

Create a new key:

  • Local ID: cats.net
  • Remote ID: dogs.net
  • Pre-shared key: same as on FortiGate

Shared key


Creating the IPsec Connection

Go to: VPN → IPsec → Connections → Add

General Settings

  • IKE Version: IKEv2
  • Proposals: AES-256 / SHA-256
  • Local Address: OPNsense WAN IP (private is fine)
  • Remote Address: FortiGate public IP or DNS name (e.g. example.lab)
  • Aggressive Mode: Enabled
  • Unique: Replace

Phase 1 setup


Authentication

Under Local Authentication:

  • Local: shared key associated with cats.net
  • Remote: shared key associated with dogs.net

Phase 2 (Child SA)

  • DPD Mode: Start
  • ESP Proposal: AES-256 / SHA-256
  • Local Network: 10.10.30.0/24
  • Remote Network: 172.16.1.0/24

Save and apply the configuration.


Verification

Once both sides are configured, verify tunnel status.

Why Dialup User and Peer ID Matter

When one side of an IPsec tunnel is behind NAT, IP-based peer identification breaks down. The public source IP may change or be unknown entirely.

Using Dialup User allows the FortiGate to accept incoming IPsec connections from peers with dynamic or unknown IP addresses.

Security is preserved through Peer ID–based authentication:

  • The peer proves its identity using a hostname-style identifier
  • The identifier must match on both sides
  • The pre-shared key must also match

In short:

NAT breaks IP-based identification.
Peer IDs restore secure identity verification.

Why Dial User  + Peer ID