write-configs

SKILL.md

Fortinet: Writing Configs

Overview

This skill guides generating correct, ready-to-paste FortiOS CLI configuration blocks. Use it when the user asks to "add", "create", "change", "generate", or "update" FortiGate config.

See fortinet:read-configs for: fleet inventory, file paths, how to read existing config to inform what you're writing.

Always read the relevant existing config section before generating changes. You need to know existing object names, interface names, and IP schemes before producing CLI.

FortiOS CLI Conventions

  • Named objects (interfaces, VPN tunnels, addresses): edit "quoted-name"
  • Indexed objects (firewall policies, static routes): edit <next-available-integer> — check existing config for the highest current integer, then add 1
  • Always end entries with next, end sections with end
  • Omit fields you aren't changing — FortiOS preserves existing values
  • To modify an existing object: config <section>edit "existing-name"set <fields>nextend
  • Never include set uuid — FortiOS auto-generates UUIDs
  • Encrypted secrets appear as ENC <base64> in snapshots — never copy those; generate fresh PSKs

Firewall Policies

Required fields for a new accept policy:

config firewall policy
    edit <N>
        set name "descriptive-name"
        set srcintf "SOURCE-INTERFACE"
        set dstintf "DEST-INTERFACE"
        set srcaddr "ADDRESS-OR-GROUP"
        set dstaddr "ADDRESS-OR-GROUP"
        set action accept
        set schedule "always"
        set service "SERVICE-OR-ALL"
        set logtraffic all
    next
end

Adding UTM inspection (required for internet-bound policies):

        set utm-status enable
        set inspection-mode flow
        set profile-type single
        set profile-protocol-options "default"
        set ssl-ssh-profile "certificate-inspection"
        set av-profile "default"
        set webfilter-profile "default"
        set ips-sensor "default"
        set application-list "default"

Policy integer N: Grep existing config firewall policy block, find the highest edit N, use N+1.

Policy ordering: New policies are appended at the bottom. To reorder after creation: move N after M or move N before M.

Interfaces and VLANs

Modify an existing physical interface:

config system interface
    edit "PORT-NAME"
        set description "human-readable description"
        set ip 10.X.X.X 255.255.255.0
        set allowaccess ping https ssh
    next
end

Create a VLAN subinterface:

config system interface
    edit "PORT.VLANID"
        set vdom "root"
        set type vlan
        set vlanid <ID>
        set interface "PARENT-PORT"
        set ip 10.X.X.X 255.255.255.0
        set allowaccess ping https ssh
        set description "VLAN description"
    next
end

allowaccess values: ping, https, ssh, http, fgfm, snmp

  • Management interfaces: ping https ssh
  • WAN/transit interfaces: ping only (or omit entirely)
  • Never include telnet — plaintext, never appropriate

Static Routes

config router static
    edit <N>
        set dst 10.X.X.X 255.255.255.0
        set gateway 10.X.X.1
        set device "INTERFACE-NAME"
        set distance 10
        set priority 1
        set comment "why this route exists"
    next
end

Default route:

config router static
    edit <N>
        set dst 0.0.0.0 0.0.0.0
        set gateway <ISP-GATEWAY-IP>
        set device "external"
        set distance 10
    next
end

VPN IPsec

Phase 1 (IKEv2, strong crypto — use for all new tunnels)

config vpn ipsec phase1-interface
    edit "TUNNEL-NAME"
        set type static
        set interface "external"
        set ike-version 2
        set proposal aes256-sha256
        set dhgrp 14
        set authmethod psk
        set peertype any
        set remote-gw <REMOTE-IP>
        set psksecret <SHARED-SECRET>
        set dpd on-demand
        set auto-negotiate enable
        set keylife 28800
        set comments "Description of this tunnel"
    next
end

Phase 2

config vpn ipsec phase2-interface
    edit "TUNNEL-NAME"
        set phase1name "TUNNEL-NAME"
        set proposal aes256-sha256
        set dhgrp 14
        set pfs enable
        set keylifeseconds 3600
        set src-subnet 0.0.0.0 0.0.0.0
        set dst-subnet 0.0.0.0 0.0.0.0
    next
end

Crypto standards for all new tunnels:

  • Encryption: aes256 (minimum aes128)
  • Hash: sha256 or sha384 — never md5, never sha1 alone
  • DH group: 14 minimum (2048-bit); prefer 19 or 20 for ECDH
  • IKE version: always 2

Do not copy existing fleet tunnel settings as a template. Some existing tunnels use aes128-sha1 and dhgrp 2 — those are known weak configs flagged by the audit skill.

Multi-Site Output

When the same change applies to multiple sites:

  1. Read the relevant section from each site's config to find site-specific values (IPs, interface names, current policy count)
  2. Generate one CLI block per site, labeled with a comment header
  3. Format:
# ============================================================
# CDW (cdwfw01)
# ============================================================
config system interface
    edit "external"
        set description "WAN uplink"
    next
end

# ============================================================
# DAL (dalfw01)
# ============================================================
config system interface
    edit "external"
        set description "WAN uplink"
    next
end

Verification Reminder

Always append this at the end of generated config output:

# VERIFY BEFORE APPLYING
# 1. SSH to the firewall
# 2. show <relevant section>  — confirm current state
# 3. Paste the config above
# 4. show <relevant section>  — confirm changes applied
# 5. To revert: re-enter the section and restore previous values
Weekly Installs
1
First Seen
Mar 3, 2026
Installed on
amp1
cline1
opencode1
cursor1
kimi-cli1
codex1