Skip to content

Releases: dannyota/fortimgr

v1.3.0

Choose a tag to compare

@dannyota dannyota released this 18 Apr 17:43
v1.3.0
441aa4d

Large read-only FlatUI coverage expansion based on live GUI discovery. All
additions are backwards-compatible.

Added

  • Device assigned package mapping via ListDeviceAssignedPackages.
  • Device dashboard summary/install context via DeviceSummary.
  • Explicit Device.HATopology while preserving legacy Device.HAMode.
  • Workflow step logs via ListWorkflowLogs.
  • Firmware upgrade path matrix via ListFirmwareUpgradePaths.
  • Device PSIRT/vulnerability summary via DevicePSIRT.
  • Static IPv6 routes via ListStaticRoutes6.
  • Device-scoped networking methods: DeviceDNS, ListDeviceDDNS,
    DeviceIPAM, SDWANSettings, ListSDWANMembers, ListSDWANServices, and
    ListSDWANDuplication.
  • Firewall object methods: ListAddresses6, ListAddressGroups6,
    ListVirtualIPGroups, ListVirtualIPs6, ListVirtualIPGroups6,
    ListIPPools6, ListIPPoolGroups, ListInternetServiceCustom,
    ListInternetServiceCustomGroups, ListInternetServiceGroups,
    ListInternetServiceNames, ListFDSDBInternetServices, and
    ListScheduleGroups.
  • Live raw-vs-SDK capture test behind the livecapture build tag. Captures are
    written only under ignored samples/ paths.

Changed

  • ListDeviceFirmware now maps the stable fields present in the live firmware
    management rows, including model/platform IDs, upgrade keys, group name,
    status, and model-device metadata.
  • Internal FlatUI transport code now shares the common POST/request handling
    path for forward and flatui_proxy.
  • Smoke coverage now includes the newly supported resources and avoids printing
    the configured FortiManager address.

Compatibility notes for existing fields

  • Device.HAMode is unchanged and remains the legacy compatibility field using "standalone" / "master" / "slave". New code should use the added Device.HATopology field for HA topology and Device.HARole for role.
  • DeviceFirmware keeps its existing fields, but ListDeviceFirmware now maps the fuller live firmware-management row. Existing fields such as CurrentVersion, CurrentBuild, UpgradeVersion, CanUpgrade, Connected, and LicenseValid may now be accompanied by additional populated metadata on the same struct.
  • No existing public fields were renamed or removed in this release.

v1.2.2

Choose a tag to compare

@dannyota dannyota released this 14 Apr 19:52
v1.2.2
1d9cd82

Retracts all versions prior to v1.2.1. No code changes from v1.2.1.

All prior versions included ListVDOMs which used a JSON-RPC endpoint that does not work through the FlatUI transport.

v1.2.1

Choose a tag to compare

@dannyota dannyota released this 14 Apr 19:39
v1.2.1
0a774d6

Breaking Changes

Removed ListVDOMs and VDOM type

ListVDOMs used /dvmdb/device/{device}/vdom, a JSON-RPC endpoint that does not work through the FlatUI transport — callers always received a permission error regardless of account privileges.

Migration: VDOM names are available as string fields on existing types:

  • Interface.VDOM
  • NormalizedInterfaceMapping.VDOM
  • PackageInstallStatus.VDOM

Pass the vdom name directly to ListInterfaces(device, vdom) and ListStaticRoutes(device, vdom) as a string. Use "" for the global/default scope.

v1.2.0

Choose a tag to compare

@dannyota dannyota released this 14 Apr 06:48
v1.2.0
56d54d2

What's New

Per-Policy Revision History

Two new methods expose FortiManager's per-object revision tracking (_objrev endpoint):

  • ListPolicyRevisions(ctx, adom, pkg, policyID) — returns the full revision history for a single firewall policy: who changed it, when, what action was taken, a human-readable change note, and the complete policy configuration snapshot at each revision point.

  • ListPolicyRevisionCounts(ctx, adom, pkg) — returns a map of policy ID → revision count across all policies in a package, useful for identifying heavily-modified policies without fetching full histories.

Use Cases

  • Field-level audit trail for individual firewall policies
  • Detecting silent policy modifications (schedule extensions, address changes) bundled inside unrelated workflow sessions
  • Compliance reporting on policy change frequency and authors

v1.1.0

Choose a tag to compare

@dannyota dannyota released this 13 Apr 05:22
v1.1.0
ede6443

v1.1.0 delivers three related themes: transparent pagination for 30 List methods, a change-management audit trail (revisions + workflow sessions + normalized interface abstraction), and device license metadata + credential-exclusion hardening on ListDevices. Every v1.0.3 symbol stays intact; all additions are backwards compatible.

Pagination (30 of 33 List* methods)

Every List method that queries a range-paginatable FortiManager endpoint now transparently fetches every page (1000 rows per forward request by default) and returns the concatenated result. Previously the SDK passed no range parameter at all — which silently truncated results on busy ADOMs where implicit row caps kicked in. If your deployment has ADOMs with thousands of policies, addresses, services, revisions, or workflow sessions, v1.1.0 is the first release that reliably returns all of them.

  • ListOption — new variadic option type accepted by 30 List methods.
  • WithPageSize(n) — override the default page size (valid 1..10000).
  • WithPageCallback(fn) — register a progress callback invoked after each page is fetched. Receives (cumulative count, 1-based page number).
  • Session expiry during pagination is handled transparently by the existing retry-on-expiry path — page 2 gets a re-login automatically.
  • Termination is robustgetPaged stops on 4 conditions: under-full page (normal), over-full page (endpoint ignored range), byte-identical page 2 (endpoint ignored offset on a same-size dataset), or a hard safety cap of 10000 iterations.

Three methods explicitly do NOT accept pagination options

Method Reason
ListADOMs(ctx, all ...bool) Variadic collision with existing all ...bool; ADOM count is license-capped (~20–100 max) so paging is unnecessary.
ListDeviceFirmware(ctx) Uses flatui_proxy with a grouping method call; not a range-paginatable list.
ListPackageInstallStatus(ctx, adom, pkg) FortiManager design limitation: /pm/config/adom/{adom}/_package/status ignores the range parameter — passing range:[999999,1] still returns the full dataset. Accepting page options would be misleading.

Example

```go
addrs, _ := client.ListAddresses(ctx, "root",
fortimgr.WithPageSize(500),
fortimgr.WithPageCallback(func(fetched, page int) {
log.Printf("fetched %d addresses so far (page %d)", fetched, page)
}),
)
```

Audit trail + ADOM interface abstraction (3 new methods)

  • ListADOMRevisions(ctx, adom, opts...) — returns the ADOM revision history from /dvmdb/adom/{adom}/revision. Each entry has Version, Name, Desc, CreatedBy, CreatedAt, Locked. Joins against WorkflowSession.RevisionID for per-change-request traceability.
  • ListWorkflowSessions(ctx, adom, opts...) — returns the workflow audit trail from /dvmdb/adom/{adom}/workflow. Captures who created each change request, who submitted it, who audited/approved it, which revision it produced, and a best-effort State string.
  • ListNormalizedInterfaces(ctx, adom, opts...) — returns the ADOM-level normalized interface abstraction from /pm/config/adom/{adom}/obj/dynamic/interface. Each normalized name's _scope array is fanned out into one NormalizedInterfaceMapping per (device, vdom) pair for easy downstream iteration.

Device license metadata + ListDevices security hardening

  • Device struct — 10 new flat License* fieldsLicenseExpire, LicenseOverdueSince, LicenseMaxCPU, LicenseMaxRAM, LicenseUTMEnabled, LicenseType, LicenseInstalledAt, LicenseLastSync, LicenseRegion, LicenseFlags. Populated from the same /dvmdb/adom/{adom}/device response — no new API calls. None of these fields carries the license activation key — FortiManager does not persist activation keys, only status, capacity, expiry, and region metadata.
  • ListDevices fields allowlist — switched to getPaged with a server-side fields allowlist on every per-page request. Encrypted device credentials (adm_pass, private_key, psk) never transit the wire. A compile-time-enforced test guards against future edits that might accidentally add a credential to the allowlist.

Tests

13 pagination subtests cover multi-page assembly, boundary conditions, callbacks, range injection, session expiry mid-pagination, endpoint-ignores-range (both over-full and exact-size variants), and error propagation. Dedicated per-method tests for revisions, workflow, and normalized interfaces cover edge cases like zero timestamps, unknown state ints, unmapped normalized interfaces, and scope fan-out.

Full changelog: v1.0.3...v1.1.0

v1.0.3

Choose a tag to compare

@dannyota dannyota released this 13 Apr 02:43
v1.0.3
860ff88

Downstream-requested improvements for a security-audit data warehouse — policy install status, richer device sync state, per-member HA role, and HA cluster member visibility. Fully backwards compatible: every v1.0.2 symbol stays intact, all additions go at the end of existing structs so positional literals keep compiling.

Added

  • ListPackageInstallStatus(ctx, adom, pkg string) ([]PackageInstallStatus, error) — new method hitting /pm/config/adom/{adom}/_package/status. Distinguishes package assignment (device on scope list) from actual installation (config pushed and running on the FortiGate). pkg is optional — empty returns every package in the ADOM, non-empty filters server-side via a filter: ["pkg","==",pkg] clause. PackageInstallStatus fields: ADOM, Package, Device, VDOM, Status ("installed" / "modified" / "never" / "unknown" / "imported").

  • Device struct additionsHostname, ConfStatus ("unknown" / "insync" / "modified"), DevStatus ("none" / "auto_updated" / "installed" / 13 others), LastChecked (time.Time, zero when last_checked==0), LastResync (same), HARole ("" / "master" / "slave"), HAMembers ([]HAMember). All populated from the existing /dvmdb/adom/{adom}/device response — no new API calls. HARole is derived by matching the device name against ha_slave[].

  • HAMembers + HAMember type — surface every HA cluster member (including the standby) that FortiManager knows about for a given device record. FortiManager models each HA cluster as a single top-level device entry with Name/Hostname set to the primary's hostname — ListDevices has never returned standbys as separate rows and still doesn't. HAMembers is the only place where passive members appear. Each entry carries Name, SerialNumber, Role ("master" / "slave"), Status ("online" / "offline"), and ConfStatus. Empty for standalone devices.

  • getExtra[T] internal helper — private generic wrapper alongside get[T]; forwards a GET whose params[0] merges extra fields (filter, option, …) into the payload. Used by ListPackageInstallStatus; existing call sites of get[T] are untouched.

  • Enum mapsconfStatuses, devStatuses, haRoles with raw-int passthrough for unmapped values (forward-compatible with future FortiManager schema additions).

  • unixToTime helper — converts int/float64/string Unix timestamps (including nil / 0) to time.Time, returning the zero value for "never" semantics.

Notes on HAMode (legacy field unchanged)

The existing Device.HAMode still maps the raw ha_mode int via the legacy deviceHAModes table ("0": "standalone", "1": "master", "2": "slave") — semantically this conflates topology and role, but behavior is preserved for v1.0.x callers. New code should prefer HARole for the per-member role, iterate HAMembers for the full cluster view, and treat HAMode as opaque until a future major version where HAMode is cleaned up to mean topology only ("standalone" / "a-p" / "a-a").

Known gaps (planned for v1.1.0)

The /pm/config/adom/{adom}/_package/status endpoint does not expose RevisionDeployed, RevisionLatest, LastInstallTime, ModifyState, or PendingChanges — the live FortiManager API only returns the aggregate status string. Callers that need those details should join against ADOM revision history (ListADOMRevisions — shipping in v1.1.0 along with workflow sessions, normalized interfaces, SDN connectors, ISDB, and traffic shapers).

Full changelog: v1.0.2...v1.0.3

v1.0.2

Choose a tag to compare

@dannyota dannyota released this 13 Apr 01:37
v1.0.2
c657a88

Friendlier IPsec naming matching the FortiGate GUI. Fully backwards compatible — no v1.0.1 symbol was renamed or removed.

Added

  • IPSecTunnel — zero-cost type alias for IPSecPhase1 (type IPSecTunnel = IPSecPhase1). Values are interchangeable with no conversion.
  • ListIPSecTunnels(ctx, adom) — one-line wrapper around ListIPSecPhase1. Shares all logic via the type alias.
  • IPSecSelector — new struct mirroring IPSecPhase2 with one field rename: Phase1NameTunnel. Kept as a distinct type (not an alias) so renaming the field on IPSecPhase2 isn't required and v1.0.1 callers of .Phase1Name keep compiling.
  • ListIPSecSelectors(ctx, adom) — delegates to ListIPSecPhase2 to reuse the HTTP/JSON/mapping path, then copies each result into an IPSecSelector with the renamed field. Single point of truth for the API call.

Rationale

Phase 1 / Phase 2 are IKE-RFC terms that force users to translate in their heads. The FortiGate GUI calls them "tunnel" and "selector", so matching that makes call sites read naturally:

```go
// Before
phase1, _ := c.ListIPSecPhase1(ctx, "root")
phase2, _ := c.ListIPSecPhase2(ctx, "root")
for _, p := range phase2 { fmt.Println(p.Phase1Name) }

// After
tunnels, _ := c.ListIPSecTunnels(ctx, "root")
selectors, _ := c.ListIPSecSelectors(ctx, "root")
for _, s := range selectors { fmt.Println(s.Tunnel) }
```

Full changelog: v1.0.1...v1.0.2

v1.0.1

Choose a tag to compare

@dannyota dannyota released this 13 Apr 01:01
v1.0.1
a7e6fcc

Bug fixes discovered while running the SDK against a live restricted-admin session.

Fixes

  • ListADOMs() now filters to the session's accessible ADOMs by default. Previously it returned every ADOM on the box (including factory presets like FortiAnalyzer, FortiMail, FortiWeb, etc.), which caused restricted admins to see ADOMs they had no scope for and fail on subsequent calls. Pass ListADOMs(ctx, true) to retain the global view for superadmin tooling. Uses /gui/sys/config to resolve the session scope (same endpoint as SystemStatus).
  • ListInterfaces(ctx, device, "") (or "global") now uses the device-wide path /pm/config/device/<dev>/global/system/interface, returning every interface across all VDOMs in one call with each carrying its own vdom field. Restricted admins cannot read /dvmdb/device/<dev>/vdom to enumerate VDOMs first, so this is the only viable path for them. Callers can derive the VDOM set from the returned list. Passing a specific vdom still routes to the per-VDOM path.
  • ListIPSecPhase1() / ListIPSecPhase2() corrected URL segment from vpn.ipsec (dotted) to vpn/ipsec (slash). The dotted form returned -3 Object does not exist on every FMG; the slash form is the valid path.

Smoke test

  • Rewrote the device loop to skip ListVDOMs (permission-denied for restricted admins) and instead fetch the global interface list per device, then derive the VDOM set from the result and call ListStaticRoutes once per derived VDOM.
  • Added an ADOMs: N accessible / M total diagnostic line that calls both ListADOMs() and ListADOMs(ctx, true) so the filtered/global comparison is visible at a glance.

Full changelog: v1.0.0...v1.0.1

v1.0.0

Choose a tag to compare

@dannyota dannyota released this 11 Mar 15:36
v1.0.0
6e023e4

Changelog

v1.0.0

First stable release. Read-only Go SDK for FortiManager's FlatUI API.

Core

  • Dual transport: forward for config/device endpoints, proxy for system endpoints
  • Session management with cookie jar and CSRF token handling
  • Auto-relogin on session expiry (status code -6) for both transports
  • Functional options: WithCredentials, WithInsecureTLS, WithTimeout, WithTransport, WithHTTPClient, WithUserAgent, WithX509NegativeSerial
  • Sentinel errors: ErrAuth, ErrNotLoggedIn, ErrPermission, ErrCertificate, ErrSessionExpired, ErrInvalidName
  • Input validation via validName() to prevent path injection
  • Zero external dependencies (stdlib only)

Resource Methods (29 total)

System (proxy transport)

  • SystemStatus() — hostname, version, serial number, HA mode, platform
  • ListDeviceFirmware() — firmware status for all managed devices

Device Management

  • ListADOMs() — administrative domains
  • ListDevices(adom) — managed FortiGate devices with firmware, HA, and status
  • ListVDOMs(device) — virtual domains on a device
  • ListInterfaces(device, vdom) — network interfaces with role, mode, allow access, VLAN
  • ListStaticRoutes(device, vdom) — static routing entries

Firewall Policy

  • ListPolicyPackages(adom) — policy packages with scope assignments
  • ListPolicies(adom, pkg) — firewall rules per package

Firewall Objects

  • ListAddresses(adom) — address objects (ipmask, iprange, fqdn, geography, wildcard)
  • ListAddressGroups(adom) — address groups
  • ListServices(adom) — custom service definitions
  • ListServiceGroups(adom) — service groups
  • ListVirtualIPs(adom) — virtual IP / port forwarding
  • ListIPPools(adom) — NAT IP pools
  • ListZones(adom) — system zones with intrazone traffic setting

Scheduling

  • ListSchedulesRecurring(adom) — recurring schedules
  • ListSchedulesOnetime(adom) — one-time schedules

Security Profiles

  • ListAntivirusProfiles(adom) — scan mode, feature set, logging options
  • ListIPSSensors(adom) — extended log, botnet scanning, malicious URL blocking
  • ListWebFilterProfiles(adom) — inspection mode, content logging, FTGD error logging
  • ListAppControlProfiles(adom) — deep inspection, unknown/other app actions
  • ListSSLSSHProfiles(adom) — cert mode, MAPI/RPC over HTTPS, ALPN support

User & Authentication

  • ListUsers(adom) — local users
  • ListUserGroups(adom) — user groups with member lists
  • ListLDAPServers(adom) — LDAP server configurations
  • ListRADIUSServers(adom) — RADIUS server configurations

VPN

  • ListIPSecPhase1(adom) — IPSec Phase 1 interfaces
  • ListIPSecPhase2(adom) — IPSec Phase 2 interfaces

Type Conversion

  • Handles FortiManager's inconsistent JSON types (string, int, float64, array)
  • Enum mapping from numeric API values to named FortiOS strings
  • Subnet formatting with automatic dotted-mask to CIDR conversion
  • Host addresses (/32) rendered without prefix length

v0.1.0: Initial Release

Choose a tag to compare

@dannyota dannyota released this 11 Mar 14:14
v0.1.0
0ed47a2

fortimgr v0.1.0

Read-only Go SDK for FortiManager using the FlatUI (Web UI) API.

Features

  • Session auth with CSRF tokens, auto-relogin on session expiry
  • Functional optionsWithCredentials, WithInsecureTLS, WithTimeout, WithTransport, WithHTTPClient, WithUserAgent, WithX509NegativeSerial
  • 12 resource methods across 8 categories:
    • ListADOMs
    • ListDevices
    • ListPolicyPackages, ListPolicies
    • ListAddresses, ListAddressGroups
    • ListServices, ListServiceGroups
    • ListSchedulesRecurring, ListSchedulesOnetime
    • ListVirtualIPs
    • ListIPPools
  • Enum mapping — numeric API values converted to named FortiOS strings
  • Input validation — ADOM/package names restricted to safe characters
  • Comprehensive tests — httptest mocks, table-driven conversion tests

Install

go get danny.vn/fortimgr@v0.1.0

Requires Go 1.24+.