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.1.tar.gz (79.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.1-py3-none-any.whl (56.9 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: nautobot_ssot_fortinet-2026.5.18.1.tar.gz
  • Upload date:
  • Size: 79.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.1.tar.gz
Algorithm Hash digest
SHA256 da011df9cd6c70505d5a29d0fdbbcb03f4e546801184358226b2accb101f9709
MD5 4d96f96d52ddbd233970ee79ec6fc9e3
BLAKE2b-256 ec67b5e1563201ee6874b2908fa5718525f73b3f15e3420dbdeef5e682ae3170

See more details on using hashes here.

File details

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

File metadata

  • Download URL: nautobot_ssot_fortinet-2026.5.18.1-py3-none-any.whl
  • Upload date:
  • Size: 56.9 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.1-py3-none-any.whl
Algorithm Hash digest
SHA256 fac362403de1b5d1a8caabb6588d3cdf42071edc19b439f31819e288bd1634ca
MD5 a12902ad71b93d2744e9cfbfbf7356bf
BLAKE2b-256 a509124a1034b243cc8530fdd7aed6b643b1eac5ee533d31e85b8ba96ea6bd2c

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