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.3.tar.gz (85.8 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.3-py3-none-any.whl (59.6 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: nautobot_ssot_fortinet-2026.5.18.3.tar.gz
  • Upload date:
  • Size: 85.8 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.3.tar.gz
Algorithm Hash digest
SHA256 74b448d391e82d1f1c84af92f6d5c22f32099ee24c506c647a8efdda085b57c0
MD5 ca8ac2c4dc4d6ac01e794c2f235107ef
BLAKE2b-256 bf8b322de8f46494fba6a1891c9d4312e496e68e02b08f63c4c07e639c643c85

See more details on using hashes here.

File details

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

File metadata

  • Download URL: nautobot_ssot_fortinet-2026.5.18.3-py3-none-any.whl
  • Upload date:
  • Size: 59.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.3-py3-none-any.whl
Algorithm Hash digest
SHA256 e6b2bb6a77811ff8e474b7ce8188f7f184fb62acb8f5cfd8ef8c224615d321d9
MD5 69303ae442b33916f989e67a024e487a
BLAKE2b-256 745477a01748a750f20859ea5d9c466ffa0242399d8d566b2b4e66dfef7ad3ab

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