Valkyrian Labs logo

Profiles and credentials

One profile per FortiGate - create the first, add, edit, rename and copy more, choose where each API key comes from, and check them with forti doctor.

Profiles and credentials

forti keeps one profile per FortiGate. A profile holds how to reach the device (host, port, VDOM, TLS files), its operating mode and a reference to its API key, never the key itself. All profiles live in one file, ~/.forti/config.json in the forti home, so managing ten firewalls is the same as managing one.

Your first profile

1forti configure

forti asks for a profile name (Enter takes default), the host, the admin HTTPS port, an optional VDOM, CA bundle, connect-to address and client certificate, where the API key comes from (it is typed with echo off), and the operating mode. Before saving, it checks the profile with one read of monitor/system/status. If the check fails, nothing is written; when it fails at the terminal for a network reason, forti offers to save the profile anyway, so a typed key is not lost. The first profile becomes the default.

Authentication failures are never retried

FortiOS locks an API user out after a few failed attempts, so forti configure makes one attempt and stops on a rejected key. Check the key, the API user's trusted hosts and its admin profile first.

Non-interactively (scripts, provisioning), pass --profile and the settings. Pick where the API key comes from:

The key comes from standard input and is kept in the secret store.

1op read op://ops/fw01/api-key | forti configure --profile fw01 --host fw01.example.net \2    --ca-bundle ~/certs/fw01-ca.pem --api-key-stdin --default

--skip-check saves without contacting the device (for example while it is offline).

Several FortiGates

Every command works on one profile: the one named by --profile/-p, else the default profile, else the only one.

1forti devices                                  # every profile; the default is marked *2forti -p fw02 get monitor/system/status3forti get firewall/policy --profile branch-ber --fields policyid,name,action4forti profile default fw02                     # change which one commands use by default

Add another device

Most FortiGates in a fleet share settings: port, VDOM, CA bundle, timeouts, mode. Start the new profile from an existing one, change what differs, then give it its own key:

1forti profile copy fw01 fw02                   # settings only: no API key, no client certificate2forti profile set fw02 host=fw02.example.net3forti configure --profile fw02                 # prompts, prefilled from fw02; type fw02's key

profile copy never copies the API key or the client certificate and its password: they belong to the source device. It does copy everything else, including resolve_ip and ca_bundle, so check that those fit the new device (forti profile show fw02). To give the new device a client certificate, run forti cert create --profile fw02 (Client certificates).

Change settings

forti profile set changes settings without prompts and without contacting the device:

1forti profile set fw02 host=fw02.example.net port=84432forti profile set lab vdom=root timeout_ms=600003forti profile set lab vdom=                    # an empty value restores the default
KeyValue
hosthost name or address, without scheme or port
portadmin HTTPS port, 1 to 65535
vdomVDOM for every request
ca_bundlePEM CA bundle (checked for readability, stored as an absolute path)
client_certPKCS#12 client certificate; clearing it (client_cert=) also clears its password
resolve_ipconnect to this IPv4 or IPv6 address while verifying TLS for host
connect_timeout_ms, timeout_ms1 to 3600000
backup_before_criticaltrue or false
moderead-only, guarded or autonomous

Values are checked exactly as config.json is when it loads. Secrets are not settings: an API key or a password goes through forti configure. Changing host or port re-binds the profile's stored secrets to the new address (only secrets bound to the old address; see Stored keys are bound to their device).

To change settings and check them against the device, or to change where the key comes from, run forti configure --profile NAME. On an existing profile, every prompt starts from the current value (including an API key command), and Enter keeps it. With options instead of prompts, only the options you give change:

1forti configure --profile fw01 --port 8443     # checks the device, keeps everything else

Rename a profile

1forti profile rename lab fw-lab

The rename takes everything along: stored secrets (Keychain or file store) move to the new name and stay bound to the same device, ~/.forti/certs/lab/ moves to certs/fw-lab/ with every profile path that pointed into it, and the default follows. A certificate made by forti cert create keeps its CN (forti-lab), so the FortiGate needs no change. Plans made before the rename name the old profile: plan them again.

Remove a profile

1forti profile remove fw-lab

remove deletes the profile's stored secrets first; if the secret store refuses (a locked keychain), the profile stays so you can remove it later. When the default profile is removed and exactly one profile remains, that one becomes the default. Removing a profile never contacts the device, and it does not delete certs/<profile>/ (use forti cert remove first).

Several devices and AI agents

forti-mcp reads the same profiles. An agent lists them with forti_discover {"what": "devices"} and names one in each call, so one registration covers every FortiGate, each under its own mode. To pin forti-mcp to one profile, register it with forti install mcp --profile NAME; see AI agent integration.

Profile commands

1forti profile list                 # also: forti profile, forti devices2forti profile show [NAME]          # settings and where the key comes from (never the key)3forti profile set NAME key=value...4forti profile rename OLD NEW5forti profile copy SOURCE NEW6forti profile default NAME7forti profile mode [NAME] guarded  # read-only | guarded | autonomous8forti profile remove NAME
  • profile show reports the key's source and, for the secret store, whether a key is stored and bound to this host and port (api_key_stored, api_key_bound); for an environment source, whether the variable is set (api_key_set).
  • profile mode without a name changes the selected profile (--profile, else the default).
  • No profile command contacts the device.

configure options

OptionMeaning
--profile NAMEprofile name: 1 to 64 letters, digits, - and _ (required with other options)
--host HOSThost name or address. No scheme and no port: fw.example.net, 192.0.2.1, [2001:db8::1]
--port Nadmin HTTPS port, 1 to 65535 (default 443)
--vdom NAMEVDOM for every request (default: the device's default VDOM)
--ca-bundle PATHPEM file of CA certificates that verify the device's certificate
--resolve-ip IPconnect to this IPv4 or IPv6 address, while still verifying the certificate for --host
--client-cert PATHPKCS#12 client certificate, when the device requires one
--api-key-stdin / --api-key-env VAR / --api-key-command CMDwhere the API key comes from (exactly one)
--cert-password-stdin / --cert-password-env VAR / --cert-password-command CMD / --cert-password-file PATHthe client certificate's password (needs a client certificate)
--mode MODEread-only (default), guarded or autonomous
--defaultmake this the default profile
--skip-checksave without the verification read

A new profile needs --host and an API key source. Paths are checked for readability and stored as absolute paths, so later commands do not depend on the working directory. Standard input carries only one secret: with --api-key-stdin, take the certificate password from another source.

Autonomous mode

Choosing autonomous prints a notice of what that mode allows. Read Choosing autonomous mode first.

Credential sources

An API key is never accepted as an argument value, because argument lists are visible to other users and end up in shell history. It comes from one of these sources:

SourceStored in config.jsonWhere the secret lives
secret store (--api-key-stdin, or typed at the prompt){"source": "store"}macOS: the login Keychain, service forti, account <profile>/api-key. Elsewhere: ~/.forti/credentials/<profile>.api-key, mode 0600
environment (--api-key-env VAR){"source": "env", "env": "VAR"}the variable, read on every use
command (--api-key-command CMD){"source": "command", "command": [argv...]}whatever the command reads; the first line of its output is the key
file (--cert-password-file PATH){"source": "file", "path": "/abs/path"}a file only you can read; its first line is the secret

A client certificate password uses the same sources, under the account <profile>/cert-password. The file source has a forti configure option for the certificate password only (forti cert create sets it up); in config.json it works for the API key as well. A file source must be a regular file, not a symbolic link, owned by you, with no access for group or others (chmod 600).

A credential command runs on every use, without a shell (found on PATH), with standard input from /dev/null. Its standard error reaches you, so a password manager can ask to be unlocked on the terminal. It must exit with status 0.

Stored keys are bound to their device

A key in the secret store is bound to the profile's host:port. forti releases it only to a profile that still points there, so another config.json that reuses the profile name cannot send your key to a different host. forti configure writes the binding, and both forti configure and forti profile set re-bind a kept key when you change the host or port. Keys from the environment, a command or a file are not stored, so they are not bound. See Security notes.

FORTI_SECRET_STORE=file selects the 0600 file store on macOS too, for example on a Mac reached over SSH where the login keychain is locked. FORTI_SECRET_STORE=keychain asks for the Keychain explicitly. Use the same setting for configuring and for running commands. On Linux the file store is the default.

macOS Keychain notes

  • The item belongs to the application that created it. Another binary, such as forti-mcp, or forti after an upgrade, triggers a one-time macOS prompt. Choose Always Allow.
  • Without a GUI session (SSH, launchd jobs) macOS cannot show that prompt, and a locked keychain cannot be unlocked interactively. forti reports this as a configuration error with a hint. Unlock with security unlock-keychain, or use --api-key-env, --api-key-command or FORTI_SECRET_STORE=file.
  • Inspect or delete items in Keychain Access by searching for forti.

TLS

Verification is always on

Certificate verification is always on, and no option, environment variable or config key disables it.

A FortiGate with a self-signed or private-CA certificate needs one of:

  • --ca-bundle PATH: export the device's CA certificate (or its self-signed certificate) as PEM and point the profile at it.
  • --resolve-ip IP: when the certificate names a host (fw01.example.net) that does not resolve from where you are, keep that name as --host and connect to the address with --resolve-ip. The certificate is still checked against the name.

forti configure and forti doctor tell a DNS failure, a refused connection, a timeout, an untrusted certificate and a failed handshake apart, and suggest the matching fix.

For a FortiGate that should also require a client certificate from forti, see Client certificates.

Checking a profile

1forti doctor [--profile NAME]

Ordered checks, each reported as ok, warn, fail, skip or info:

CheckWhat it verifies
homethe forti home; a pre-0.2 setup is a warning, with forti home migrate as the fix (Configuration files)
configthe configuration directory and config.json exist; the files are private (credential files readable by others fail, loose directory or config modes warn)
profilethe selected profile loads and is valid
secretsthe API key (and certificate password) resolve. Values are never shown
tls_filesthe CA bundle and client certificate exist and are readable; the bundle contains a PEM certificate
connectionDNS, TCP and TLS, through one read of monitor/system/status
auththe device accepts the key (one attempt, never retried)
versionthe device's FortiOS version against the SDK schema version (a mismatch is a warning: reads work, attributes added or removed between versions may be missing or rejected in plans)
modethe operating mode
privilegesautonomous profiles only: whether the account looks super_admin-class (read from system/api-user)
mcpwhether forti-mcp is on PATH (information only)

Later checks are skipped when an earlier one they depend on failed. The exit status is 0 when nothing failed, otherwise that of the first failure (3 for configuration, 4 for connection or authentication). With --json, a failing run returns ok: false with the checks in error.details.checks. Run it once per device: forti doctor -p fw02.

Where profiles, secrets, certificates and plans are stored: Configuration files.