Skip to main content

Nautobot SSoT integration for Fortinet (FortiGate firewall + FortiAP wireless, bidirectional sync).

Project description

nautobot-ssot-fortinet

Bidirectional Nautobot ↔ FortiGate sync for firewall, wireless, and live runtime state — built on the Nautobot SSoT framework.

Tests Live-validated Python Nautobot

What this does

Four Nautobot Jobs, each handling one direction of the FortiGate↔Nautobot relationship:

# Job Direction Object kinds
1 FortiGate → Nautobot (firewall) Pull Addresses, address groups, services, service groups, policies + rules, NAT (VIPs)
2 FortiGate → Nautobot (wireless) Pull WirelessNetworks (SSIDs), RadioProfiles (radios), optional AP Devices
3 FortiGate Live Status Observability Real-time wifi clients, DHCP leases, ARP table — joined by MAC
4 Nautobot → FortiGate (firewall) Push Address objects (all 4 types), address groups, services, service groups

All Jobs are idempotent (re-running produces zero diffs when nothing changed) and additive-only by default (destructive deletes opt-in via a per-Job BooleanVar).

How it works

                    ┌────────────┐
                    │  Nautobot  │
                    │            │
                    └─────┬──────┘
                       ↑  │
              pull (2) │  ▼ push (1)
                       │
                    ┌────────────┐    monitor/* (live state)
                    │  FortiGate │ ────────────────────────→ Job 3
                    └────────────┘
  • Pull Jobs read cmdb/* endpoints and write to Nautobot ORM (firewall-models + core wireless). State on the FortiGate is the source of truth; Nautobot mirrors it.
  • Push Job reads from Nautobot ORM and writes to FortiGate cmdb/* endpoints. Operators edit firewall objects in Nautobot's UI; the push Job propagates them to the device.
  • Live status Job queries monitor/* endpoints for real-time observed state (connected clients, leases, ARP) and renders them in the Job result page + downloadable JSON snapshot.

Quick start

# 1. Bring up the dev stack
cp development/.env.example development/.env
# edit development/.env — set NAUTOBOT_SECRET_KEY + FortiGate credentials
make -C development up

# 2. Wait ~60s, then seed the ExternalIntegration + SecretsGroup
make -C development seed

# 3. Open https://ssot-fortinet-dev.local/ , enable the 4 Jobs in
#    /extras/jobs/ , then run them from /plugins/ssot/

For full installation steps including production deployment, see docs/admin/install.md.

Live-validated against real hardware

This integration was validated end-to-end against a FortiWiFi-61E during development. The live e2e harness (make e2e-live-firewall and make e2e-live-wireless) connects to a real FortiGate, syncs all object kinds, and asserts the second run produces zero diffs (full idempotency).

Real-world quirks discovered & handled:

FortiOS reality Translation
interface-subnet address type Treated as ipmask (FortiOS already resolves to CIDR)
Space-separated multi-port ("88 464" Kerberos) Normalized to comma ("88,464") for firewall-models' validator
ICMP6 protocol Mapped to IPv6-ICMP (IANA name used by firewall-models)
'513:512-1023' src-port qualifier (RLOGIN/RSH) Source-port stripped, destination kept
protocol: "ALL" pseudo-protocol (webproxy) Skipped — no Nautobot equivalent
protocol: "IP" + protocol-number: N Mapped to named IANA protocol (e.g. 89 → OSPFIGP)
FortiOS WTP-profile mode vs Nautobot WirelessNetwork mode Most-common platform-mode per VAP wins
FortiOS radio band strings (802.11ax-5G) Pattern-matched to 2.4GHz/5GHz/6GHz enum
FortiOS VAP security: "wep128" Lossy mapping → Open with annotation in description

See docs/user/external_interactions.md for the complete field-by-field reference.

What's required

  • Nautobot 3.1+ (for the wireless models)
  • nautobot-ssot 4.2+ (SSoT framework)
  • nautobot-firewall-models 3.0+ (firewall object target)
  • fortigate-api 2.0+ (REST client; verified against FortiOS 6.4–7.x)
  • Python 3.10–3.13

See docs/admin/compatibility_matrix.md for the detailed compatibility matrix and version pinning rationale.

What's not in scope (yet)

  • Policy/NAT push — pull works; push for those is a future iteration (the M2M dependency graph is more complex than addresses/services)
  • Source NAT (FortiOS ippool) — pull only does Destination NAT (VIPs)
  • IPv6 addresses (firewall/address6) — IPv4 only for v1
  • Multi-VDOM aware UI — Jobs scope by VDOM, but the per-VDOM dropdown in the SSoT dashboard is a Nautobot-side enhancement, not in this app

Project layout

src/nautobot_ssot_fortinet/
├── clients/fortigate.py        ExternalIntegration → FortiGateAPI factory
├── diffsync/
│   ├── models/
│   │   ├── firewall.py         Vendor-neutral DiffSync models
│   │   ├── wireless.py         Vendor-neutral DiffSync models
│   │   ├── nautobot_firewall.py     Nautobot-side CRUD (pull target)
│   │   ├── nautobot_wireless.py     Nautobot-side CRUD (pull target)
│   │   └── fortigate_target_firewall.py   FortiGate-side CRUD (push target)
│   └── adapters/
│       ├── fortigate_firewall.py       Pull source
│       ├── fortigate_wireless.py       Pull source
│       ├── fortigate_firewall_target.py    Push target (read state + write CRUD)
│       ├── nautobot_firewall.py        Pull target (= push source)
│       └── nautobot_wireless.py        Pull target
├── jobs.py                     4 Job classes
└── utils/fortios.py            Translation helpers (subnet→CIDR, security→auth, etc.)

Testing

$ pytest -q
174 passed in 0.57s

Unit tests use a MagicMock-stubbed Django (per tests/conftest.py) so they run in milliseconds without bootstrapping Nautobot. Integration coverage is provided by the live e2e harnesses in development/scripts/ — those require a real FortiGate.

Status

v0.x — bidirectional sync working against real hardware. Ready for controlled production use against a single FortiGate; multi-device deployments should validate carefully (the name-mangling convention handles cross-device uniqueness, but multi-VDOM hostnames with __ characters need attention).

Roadmap: see CHANGELOG.md.

License

Apache-2.0.

Author

Ryan Malloy — ryan@supported.systems

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

nautobot_ssot_fortinet-2026.5.18.10.tar.gz (305.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

nautobot_ssot_fortinet-2026.5.18.10-py3-none-any.whl (63.7 kB view details)

Uploaded Python 3

File details

Details for the file nautobot_ssot_fortinet-2026.5.18.10.tar.gz.

File metadata

  • Download URL: nautobot_ssot_fortinet-2026.5.18.10.tar.gz
  • Upload date:
  • Size: 305.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"EndeavourOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for nautobot_ssot_fortinet-2026.5.18.10.tar.gz
Algorithm Hash digest
SHA256 c707056aa1d5a30995c895dbe6c36be74281f14605797dd67ad6fa0bc7a29aed
MD5 3e0fbab86a7f02ae694ae96b6ea7df04
BLAKE2b-256 e4688ed1df100c93b11c78f7c78d669b4bd41fca8904707f99f5d93166cd8b7d

See more details on using hashes here.

File details

Details for the file nautobot_ssot_fortinet-2026.5.18.10-py3-none-any.whl.

File metadata

  • Download URL: nautobot_ssot_fortinet-2026.5.18.10-py3-none-any.whl
  • Upload date:
  • Size: 63.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"EndeavourOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for nautobot_ssot_fortinet-2026.5.18.10-py3-none-any.whl
Algorithm Hash digest
SHA256 7d5a39e5b728d94571156c1a62239dd6a52ec9ddc5cc770e2e4665d7a11ddfc0
MD5 5ada399f0967f4de68f4f20b563b5e9a
BLAKE2b-256 b0cb5f0003ce2822a80b5fa22c63a130385b4a3083c428720b61ac0e710be215

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page