Skip to main content

Nautobot SSoT integration for Fortinet (FortiGate firewall + FortiAP wireless, one-way: FortiGate -> Nautobot).

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.2.tar.gz (82.6 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.2-py3-none-any.whl (58.6 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: nautobot_ssot_fortinet-2026.5.18.2.tar.gz
  • Upload date:
  • Size: 82.6 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.2.tar.gz
Algorithm Hash digest
SHA256 3990def256415515955c7ed92b0e6a5df6f3ff7650fc34b7f86803871a339eb9
MD5 1af14ff3727fbf53901a8d527bfc8325
BLAKE2b-256 9884efb01f2523a820f2e26a755690cd3a84c1b2297d15b5f1d3be1d62fa813e

See more details on using hashes here.

File details

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

File metadata

  • Download URL: nautobot_ssot_fortinet-2026.5.18.2-py3-none-any.whl
  • Upload date:
  • Size: 58.6 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.2-py3-none-any.whl
Algorithm Hash digest
SHA256 40d29510d7029bd6fc6d8f34f777ff7481b02843b9e8b80ca008f00b48718088
MD5 62e436d187c59d4b30eae8daddec4577
BLAKE2b-256 577dbbdc16bfc12f509e7f2a6f13007cd9859998db566a077687596e2ac69e8f

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