# Parsing Fortinet Configs

> Parse FortiGate and FortiOS full-configuration or backup exports into the shared firewall schema. Use when input contains config/edit/set/next/end blocks, VDOM, firewall policy or address, srcintf, dstintf, UTM profiles, or VIPs, including audit, conversion, diff, summary, and explanation tasks.

- **Type:** Skill
- **Install:** `agentstack add skill-fastrevmd-lab-fwskillsshare-parsing-fortinet-configs`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [fastrevmd-lab](https://agentstack.voostack.com/s/fastrevmd-lab)
- **Installs:** 0
- **Category:** [AI & ML](https://agentstack.voostack.com/c/ai-and-ml)
- **Latest version:** 0.1.0
- **License:** Apache-2.0
- **Upstream author:** [fastrevmd-lab](https://github.com/fastrevmd-lab)
- **Source:** https://github.com/fastrevmd-lab/fwskillsshare/tree/main/skills/parsing-fortinet-configs
- **Website:** https://mechub.org

## Install

```sh
agentstack add skill-fastrevmd-lab-fwskillsshare-parsing-fortinet-configs
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Parsing Fortinet FortiGate Configurations

## Overview

Use this skill to parse Fortinet FortiGate / FortiOS backup or `show full-configuration` output into the shared vendor-neutral firewall intermediate schema. It focuses on nested `config` / `edit` / `set` / `next` / `end` blocks, including VDOMs, interfaces, zones, firewall addresses and services, policies, central SNAT, VIPs, routes, VPN, HA, profiles, and system settings.

FortiOS behavior is version- and feature-dependent. Preserve unknown blocks in `residual_raw`, capture VDOM context, and flag policy/NAT/profile constructs that cannot be mapped cleanly to the intermediate schema.

## Scope and routing

Use only for FortiGate or FortiOS block syntax. Hand off ASA/FTD `access-list`, `nameif`, or `object network` input to `parsing-cisco-configs`, PAN-OS XML or `set deviceconfig` to `parsing-palo-configs`, and Junos hierarchy or `set security` to `parsing-srx-configs`. Verify production-bound results against current device documentation and output. Downstream consumers are the audit, conversion, and diff skills.

## Input Format

FortiOS configs use a hierarchical block format:
```
config 
    edit 
        set  
        set  
        config 
            edit 
                set  
            next
        end
    next
    edit 
        ...
    next
end
```

Key syntax rules:
- Values with spaces are quoted: `set comment "Allow web traffic"`
- Multi-value fields use space-separated values: `set srcaddr "addr1" "addr2"`
- Full block-syntax rules: `references/config-format.md`

### Building the Config Tree

Parse the block format into a nested object tree:
1. Track a stack of current context (section path + edit name)
2. `config ` → push section name
3. `edit ` → push entry name (strip quotes)
4. `set  ` → store key-value at current depth
5. `next` → pop entry
6. `end` → pop section

## Extraction Pipeline

### 1. Zones and Interfaces
**Zones:** `config system zone` → `edit ` with `set interface `
**Interfaces:** `config system interface` → `edit ` with `set vdom`, `set ip`, `set type`, `set vlanid`
Additional interface fields to extract:
- `set ip6 ` (inside nested `config ipv6` sub-block) — IPv6 address
- `set mtu ` — MTU
- `set type aggregate` → type: "lag" (with `set member` for LAG members)
- `set type loopback` → type: "loopback"
- `set type tunnel` → type: "tunnel"
- `set mode dhcp` — DHCP client (no static IP)
- `set dhcp-relay-ip ` — DHCP relay server IPs
- `set allowaccess ` — management access protocols
- `set fortilink enable` — FortiLink interface (exclude from output along with child interfaces)
- `set description `
- Subinterface detection: dot-notation (`port1.100` → parent=`port1`) or VLAN-bound (`set interface ` + `set vlanid `)
- Management interface detection: names matching `/^(mgmt\d*|management\d*)/i` → `is_mgmt: true`
- After all interfaces parsed, back-populate `lag_members` on aggregate interfaces

**Critical: Interface-as-Zone Merging**
FortiGate can use interface names directly as zones in policies (via `srcintf`/`dstintf`).
If a policy references an interface name not in any zone, treat that interface as its own zone.
Merge zones and interfaces: create a zone entry for each interface used as a zone.

**Zone Building Priority 3: Unzoned Interfaces**
After explicit zones and policy-referenced interfaces, create auto-zones for any interface that has an IP address (or is a DHCP client) but was not yet assigned to a zone.

**Allowaccess Classification:**
Classify `allowaccess` values into management services (ssh, https, http, telnet, ping, snmp, netconf, fgfm, fmg-access, ftm, radius-acct, security-fabric, fabric, capwap, speed-test) and routing protocols (ospf, bgp, rip, isis, bfd). Attach to zones as `host_inbound` data.

### 2. Address Objects
Path: `config firewall address` → `edit `

Types — detect from `set type` or infer from fields:
- `set type ipmask` + `set subnet  ` → type: "subnet" (convert mask to CIDR); promote a /32 (or IPv6 /128) result to type: "host"
- `set type iprange` + `set start-ip` / `set end-ip` → type: "range", value: "start-end"
- `set type fqdn` + `set fqdn ` → type: "fqdn"
- `set type geography` + `set country ` → type: "geo" (warn: limited cross-platform support)
- `set type wildcard` + `set wildcard  ` → type: "wildcard" (preserve the ` ` wildcard value; "network" is NOT a valid schema type) with info warning
- `set type wildcard-fqdn` + `set wildcard-fqdn ` → type: "fqdn" (convert from wildcard-fqdn with info warning)

Also extract: `set comment`, `set associated-interface`.
Convert subnet mask notation (`255.255.255.0`) to CIDR (`/24`).
Auto-detect IP version.

**IPv6 Address Objects:** `config firewall address6` → `edit `
- `set ip6 ` → type: "subnet" (or "host" if /128)
**IPv6 Address Groups:** `config firewall addrgrp6` → `edit `
- `set member `

### 3. Address Groups
Path: `config firewall addrgrp` → `edit `
Extract: `set member ` (space-separated quoted names)

### 4. Service Objects
Path: `config firewall service custom` → `edit `
Extract from:
- `set protocol TCP/UDP/SCTP` + `set tcp-portrange ` / `set udp-portrange ` / `set sctp-portrange `
- `set protocol ICMP` + `set icmptype` / `set icmpcode`
- `set protocol IP` + `set protocol-number ` → preserve the IP protocol number `` (e.g. GRE=47, ESP=50) as the service object's protocol (the numeric value, or via a warning). Only emit protocol: "any" when `set protocol IP` genuinely means all IP protocols (no `protocol-number`, or `protocol-number 0`).
- `set protocol ICMP6` → protocol: "icmpv6"
- Port range format: `80` or `80-443` or `80:1024-65535` (dst:src)
- Note: A single custom service can set ANY combination of `tcp-portrange`, `udp-portrange`, and `sctp-portrange` simultaneously. Split into separate service objects — one TCP, one UDP, and/or one SCTP — according to which ranges are present.

### 5. Service Groups
Path: `config firewall service group` → `edit `
Extract: `set member `

### 6. Security Policies
Path: `config firewall policy` → `edit `

For each policy extract:
- **name** — `set name ` (FortiGate uses numeric IDs as primary key, name is optional)
- **src_zones** — `set srcintf ` (interface or zone names)
- **dst_zones** — `set dstintf `
- **src_addresses** — `set srcaddr `
- **dst_addresses** — `set dstaddr `
- **services** — `set service `
- **applications** — `set application ` (application control IDs/names)
- **action** — `set action accept` → "allow", `set action deny` → "deny"
- **log_start** — `set logtraffic-start enable` (start logging is controlled separately from `logtraffic`)
- **log_end** — `set logtraffic all`. `set logtraffic utm` logs UTM/security events only, NOT end-of-session traffic — map to log_end: false and note UTM-only logging in `metadata.warnings`
- **disabled** — `set status disable`
- **description** — `set comments `
- **schedule** — `set schedule `
- **source_users** — `set groups ` (FSSO groups)

**Policy NAT (do NOT emit a policy `nat` field — the policy schema has no `nat` field):**
FortiGate per-policy source NAT (`set nat enable`, optionally with `set ippool enable` + `set poolname `) must be translated into a `nat_rules[]` source-NAT entry, NOT a flag on the policy:
- `set nat enable` alone → source-NAT (interface/egress overload) on the policy's `dstintf`, scoped to the policy's `srcaddr`/`dstaddr`.
- `set nat enable` + `set ippool enable` + `set poolname ` → source-NAT using the named IP pool `` (cross-reference `config firewall ippool`).
- If the source-NAT intent cannot be resolved to a concrete `nat_rules[]` entry, preserve it as a `metadata.warnings` / `residual_raw` note rather than inventing a policy field.

**Default values** when fields are omitted from config:
- `action` defaults to `accept` (→ "allow")
- `logtraffic` defaults to `utm` on accept policies (→ log_end: false; UTM-event logging only)
- `status` defaults to `enable` (→ disabled: false)

**UTM / Security Profiles** — when `set utm-status enable`:
- `set av-profile ` → antivirus
- `set webfilter-profile ` → URL filtering
- `set ips-sensor ` → IPS/IDP
- `set application-list ` → application control (do NOT emit an `application-list` security-profile key — it is not a schema-supported profile key; signal app-control presence via `security_services.app_id` at device level and/or store the profile reference in `security_profile_objects` / `metadata.warnings`)
- `set ssl-ssh-profile ` → SSL inspection
- `set dnsfilter-profile ` → DNS filtering
- `set emailfilter-profile ` → email filtering
- `set dlp-profile ` → DLP
- `set profile-group ` → profile group (overrides individual profiles)

### 7. NAT Rules
**Source NAT (IP Pools):** `config firewall ippool` → `edit `
  Extract: `set startip`, `set endip`, `set type` (overload, one-to-one, fixed-port-range), `set associated-interface ` — binds pool to specific egress interface

**Central SNAT:** `config firewall central-snat-map` → `edit `
  Extract the full FortiOS central-SNAT field set so real rules are not missed:
  - `set srcintf ` — source interface(s)
  - `set dstintf ` — destination/egress interface(s)
  - `set orig-addr ` — original (pre-NAT) source addresses
  - `set dst-addr ` — destination addresses the rule matches
  - `set nat enable|disable` — whether this entry performs NAT (a disabled entry = no-NAT exemption; preserve it)
  - `set nat-ippool ` (also `set natippool` variant) — translated source pool; absent → egress-interface overload
  - `set protocol ` and `set orig-port` / `set nat-port` where present — protocol/port scoping
  Map to a `nat_rules[]` source-NAT entry (or a no-NAT exemption when `nat disable`).

**Destination NAT (VIPs):** `config firewall vip` → `edit `
  Extract: `set extip` (original dest), `set mappedip` (translated dest),
  `set extintf`, `set portforward enable` + `set extport` / `set mappedport`
  Note: VIPs are referenced in policies via `set dstaddr `

### 8. Schedules
**Recurring:** `config firewall schedule recurring` → `edit `
  Extract: `set day`, `set start`, `set end`
**One-time:** `config firewall schedule onetime` → `edit `
  Extract: `set start`, `set end`
**Group:** `config firewall schedule group` → `edit `
  Extract: `set member`

### 9. Application Mapping (L7 → Canonical)

FortiGate supports L7 application control via `set application ` on policies. These reference
FortiOS application IDs or names from the application control database.

**Extracting application references from policies:**
- `set application ` — space-separated application IDs or names
- `set application-list ` — references an application control list profile (separate from direct app match)

**Resolving FortiOS application names to canonical:**

| FortiOS Name | Canonical App | Category |
|-------------|---------------|----------|
| `HTTPS` | `https` | web |
| `HTTP` | `http` | web |
| `SSH` | `ssh` | remote-access |
| `RDP` | `rdp` | remote-access |
| `DNS` | `dns` | network-mgmt |
| `SMTP` | `smtp` | email |
| `NTP` | `ntp` | network-mgmt |
| `SNMP` | `snmp` | network-mgmt |
| `FTP` | `ftp` | file-transfer |
| `TFTP` | `tftp` | file-transfer |
| `SIP` | `sip` | voip |
| `LDAP` | `ldap` | auth |
| `Kerberos` | `kerberos` | auth |
| `SMB` | `smb` | file-transfer |
| `MySQL` | `mysql` | database |
| `MSSQL` | `mssql` | database |
| `PostgreSQL` | `postgresql` | database |
| `MongoDB` | `mongodb` | database |
| `Zoom` | `zoom` | collaboration |
| `Microsoft.Teams` | `ms-teams` | collaboration |
| `Slack` | `slack` | collaboration |
| `YouTube` | `youtube` | streaming |
| `Netflix` | `netflix` | streaming |

**On policy output:** When `set application` values are resolved, populate the policy's `apps` array
with `{ vendor_name: "HTTPS", canonical: "https", confidence: 1.0, category: "web" }`.
The `services` array keeps any `set service` matches separately.

**Application control list profiles** (`config application list`) define grouped app-control
policies. These do not map 1:1 to application groups — they are UTM profiles that filter
applications by category, risk, or specific app ID. The schema has no `application-list`
profile key — represent app-control presence via `security_services.app_id` (device-level)
and store the profile reference in `security_profile_objects` and/or `metadata.warnings`.
Do not try to decompose the list into individual apps.

**Unresolvable apps:** FortiOS numeric app IDs without a known name mapping → set `confidence: 0.0`,
preserve the ID as `vendor_name`, and warn.

### 9b. Application Groups

FortiOS does not have explicit application groups in the same way PAN-OS does. The closest
equivalent is `config application group` which groups application control signatures.

Path: `config application group` → `edit `
Extract: `set application ` — member application IDs/names.
Resolve each member to canonical. Store in `application_groups` array.

If a group contains a mix of L7 apps and port-based services, split them appropriately.

### 10. Security Profile Definitions
Parse full profile objects for reference:
- `config antivirus profile`
- `config webfilter profile`
- `config ips sensor`
- `config application list`
- `config firewall ssl-ssh-profile`

### 11. Routing
- **Static routes (IPv4):** `config router static` → `edit ` with `set dst`, `set gateway`, `set device`, `set distance`
- **Static routes (IPv6):** `config router static6` → `edit ` with same fields using IPv6 prefixes
- **BGP:** `config router bgp` — extract:
  - `set as`, `set router-id`, global `set keepalive-timer`/`set holdtime-timer`
  - Per-neighbor (in `config neighbor`): `remote-as`, `description`, `update-source`, `password` (record presence only — redact the value, never emit it), per-neighbor timers (override global), `next-hop-self`, `soft-reconfiguration`, `route-reflector-client`, `status enable|disable`
  - `config network` entries (prefix advertisements)
  - `config redistribute` with `set status enable|disable`
  - Warn: route-map/prefix-list references are not converted
- **OSPF:** `config router ospf` — extract:
  - `set router-id`, `set auto-cost-reference-bandwidth`
  - `config area`: area ID, type (stub/nssa with no-summary), default-cost, authentication
  - `config ospf-interface`: area assignment, passive flag, cost, priority, hello/dead intervals, network-type (point-to-point/broadcast), MD5 authentication with key ID (record key presence only — redact the key value)
  - `config redistribute`: source, status, metric, metric-type
  - Warn: MD5 keys in cleartext in source config (key values are never emitted in output)
- **OSPFv3:** `config router ospf6` — same structure but uses `config ospf6-interface` (not `ospf-interface`)
- **Policy routing:** `config router policy` → PBF rules

### 12. Infrastructure
- **Version:** Extract from `#config-version=:` comment line at top of config
- **System Global:** `config system global` → extract `set hostname`
- **DNS:** `config system dns` → extract `set primary`, `set secondary`, `set domain`
- **NTP:** `config system ntp` with nested `config ntpserver` → extract server entries
- **Admin Users:** `config system admin` → extract access profile (super_admin→super-admin, prof_admin→admin), SSH public keys (ssh-public-key1/2/3). Warn when users lack SSH keys.
- **HA:** `config system ha` — `set mode` (a-p/a-a), `set group-id`, `set priority`,
  `set hbdev`, `set monitor`
- **Screen/DoS:** `config firewall DoS-policy` + IPS sensor definitions
- **Syslog:** `config log syslogd setting`
- **DHCP Server:** `config system dhcp server` — extract top-level fields (`set default-gateway`, `set netmask`, `set interface`, `set domain`, `set lease-time`, `set dns-server1/dns-server2`) plus nested `config ip-range` (start-ip/end-ip) and `config reserved-address` (mac, ip, description). Derive network CIDR from gateway + netmask.
**FortiOS compound proposal parsing for VPN:**
FortiOS encodes IKE/IPsec proposals as compound strings. Do NOT assume every proposal is a simple
`encryption-integrity` pair

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [fastrevmd-lab](https://github.com/fastrevmd-lab)
- **Source:** [fastrevmd-lab/fwskillsshare](https://github.com/fastrevmd-lab/fwskillsshare)
- **License:** Apache-2.0
- **Homepage:** https://mechub.org

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-fastrevmd-lab-fwskillsshare-parsing-fortinet-configs
- Seller: https://agentstack.voostack.com/s/fastrevmd-lab
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
