Skip to content

snippet

Snippet management commands for NDCLI. Snippets are reusable configuration blocks that define users, groups, aliases, and firewall rules. Snippets are combined into templates to create complete configurations.

Snippet content is JSON for every type. The NetDefense agent talks to OPNsense through its REST API for most of them, turning each snippet’s content into the request for the matching OPNsense endpoint; for RULE and ALIAS it builds the whole object from the device’s own model — see RULE and ALIAS fields. AUTH_SERVER and AUTH_ORDER are the one exception — OPNsense has no REST API for authentication servers or the login order at all, so a dedicated on-device helper edits the underlying configuration directly instead.

TypeDescription
USERUser definition (OPNsense auth/user). password is the plaintext to set, never a hash — see USER snippets and passwords
GROUPGroup definition (OPNsense auth/group)
ALIASNetwork alias — host, network, or port set (OPNsense firewall/alias)
RULEFirewall rule (OPNsense firewall/filter)
UNBOUND_HOST_OVERRIDEUnbound DNS host override — A/AAAA/MX/TXT record
UNBOUND_DOMAIN_FORWARDUnbound DNS domain forwarding (incl. DoT)
UNBOUND_HOST_ALIASUnbound DNS host alias (CNAME-style, references a host override)
UNBOUND_ACLUnbound DNS query ACL
ZABBIX_SETTINGSZabbix Agent global settings — singleton; the agent owns the full local/main/tuning/features tree on every apply
ZABBIX_USERPARAMETERZabbix UserParameter (custom check command). Key must start with nd-
ZABBIX_ALIASZabbix item-key alias. Key must start with nd-
AUTH_SERVERExternal LDAP or Active Directory authentication server. See Authentication Servers.
AUTH_ORDERLogin-order policy for a facility (webadmin) — which authentication sources are tried, and in what sequence. See Authentication Servers.
TRUST_CACA certificate, without its private key, synced into the device’s trust store (OPNsense trust/ca). See Certificates.
TRUST_CERTCertificate and its private key, which comes from a secret variable, synced into the device’s trust store (OPNsense trust/cert). See Certificates.

Each type requires a few fields in its JSON content. Creating or updating a snippet without them is rejected, with a message naming the type and the missing field — so the problem surfaces when you author the snippet, not later when a device tries to apply it. A RULE’s action is required too, and must not be empty: OPNsense’s default action is pass, so a rule without one would let traffic through.

TypeRequired fields
ALIASname, type
RULEaction, non-empty
UNBOUND_HOST_OVERRIDEhostname, domain
UNBOUND_DOMAIN_FORWARDdomain, server
UNBOUND_HOST_ALIAShostname, domain
UNBOUND_ACLname, action
ZABBIX_ALIASkey
USER, GROUP, ZABBIX_USERPARAMETER, ZABBIX_SETTINGSas described for each type above
AUTH_SERVERname, type, host, ldap_port, ldap_urltype, ldap_protver, ldap_scope, ldap_basedn, ldap_authcn, ldap_attr_user
AUTH_ORDERfacilities
TRUST_CAname, crt
TRUST_CERTname, crt, key

Everything else in a snippet’s content is optional. Beyond the required fields:

  • RULE and ALIAS content may set any field of the device’s OPNsense rule or alias, under its API name, and a field left out takes OPNsense’s default — except a rule’s action, which must be set and non-empty at save, because OPNsense’s default is pass. A field the device’s model doesn’t have is refused when the device syncs (RULE_FIELD_UNKNOWN, ALIAS_FIELD_UNKNOWN) — see RULE and ALIAS fields. This needs agent 1.23.0 or later; a device on an older agent fails its build with SNIPPET_AGENT_TOO_OLD when a snippet uses a field that agent would drop — see Agent and OPNsense versions.
  • RULE, ALIAS and UNBOUND_* values are JSON strings in OPNsense’s own form: "443", "1", "lan,wan". enabled, and a domain forward’s forward_tcp_upstream and forward_first, may also be true or false, and an alias’s content and an ACL’s networks a list of strings. Any other value, null included, is refused on create and update, naming the field and the form to write — see Field values.
  • AUTH_SERVER, AUTH_ORDER, TRUST_CA and TRUST_CERT accept only a closed set of known keys (an unknown key is rejected) and require strict JSON with no duplicate keys. AUTH_SERVER and AUTH_ORDER also reject a uuid or refid supplied by hand, and ignore --priority entirely. TRUST_CA and TRUST_CERT reject a uuid on create, and an update may leave it out or send back the one NetDefense assigned, never another.

ZABBIX_ALIAS has one quirk worth knowing: its enabled and accept_params fields must be real JSON booleans (true / false). A quoted "true" or a 1 is rejected at create, with the same message naming the type and the field.

A USER snippet’s password is the plaintext password to set on the account — never a hash. OPNsense hashes whatever password it receives, so a hash pasted here would be hashed a second time: the account’s password would become the hash text itself, and the password you meant to set would not work.

  • Keep it in a secret variable. Write "password": "${name}" and store the value in an organization-scope secret variable, so the password never sits in the snippet’s content. A literal password also works, but it is then stored and shown exactly as written, and lands in your shell history if you type it on the command line.
  • A new account needs a password. Applying a USER snippet for an account that doesn’t exist on the device yet fails without one.
  • Omit password to keep an existing account’s password. When the account is already on the device, a snippet without password leaves its current password alone.
  • A pulled snippet has no password. From ndagent 1.20.1, snippet pull doesn’t copy an account’s password off the device; an older agent copies the account’s stored password hash, which you must remove or replace with the plaintext. Add a password before you apply the snippet to a device where the account doesn’t exist yet.
  • A hash is refused. Saving a password with the shape of a password hash fails ('password' must be the plaintext to set, not a hash), and from ndagent 1.20.1 a device refuses one an older snippet still holds, with the code USER_PASSWORD_IS_HASH — see USER snippets and passwords.
  • Quote ${...} so your shell leaves it alone. Single-quote inline --content, or put the JSON in a file and use --file — otherwise most shells try to expand $name themselves before ndcli ever sees it.
Terminal window
# Store the password once, keeping it off the command line
ndcli variable org create svc_monitoring_password --secret --value-stdin < ./password.txt
# Reference it from the snippet
ndcli snippet create svc-monitoring --type USER \
--content '{"name": "svc-monitoring", "descr": "Monitoring service account", "password": "${svc_monitoring_password}"}'

Authentication servers and login order (AUTH_SERVER / AUTH_ORDER)

Section titled “Authentication servers and login order (AUTH_SERVER / AUTH_ORDER)”

The full guide is Authentication Servers. A few things specific to authoring these two types from the command line:

  • Prerequisites: ndagent 1.19.2 or later, OPNsense 26.1.6 or later, ndcli 1.35.0 or later, and the device’s Allow All Snippet Content setting turned on — it’s off by default (see Device-Side Protection).
  • Quote ${...} so your shell leaves it alone. ldap_bindpw’s value is a literal ${variable_name} string that NetDefense resolves later — most shells try to expand $variable_name themselves before ndcli ever sees it. Single-quote inline --content, or put the JSON in a file with --file instead.
  • Local Database must be first. Every AUTH_ORDER snippet defining the webadmin facility must list Local Database as order[0], or the create/update is refused outright.
  • The consistency rule — checked on the device during sync, not when you save the snippet: every name must be an auth server delivered to the same device by any of its templates, or already configured on the device; names match exactly (case-sensitive); Local Database is always valid.
  • Servers configured by hand on the device get none of NetDefense’s guardrails — no reserved-name exclusion, no Limit-groups requirement, no protected-group check, no cleartext acknowledgement. An AUTH_ORDER snippet may still name one; check it yourself first.
  • Cleartext ("ldap_urltype": "TCP - Standard") needs an explicit "nd_allow_cleartext_ldap": true in the content, or the create is refused.
  • Attaching authored content is a lower bar than authoring it. Creating or updating AUTH_SERVER/AUTH_ORDER content needs the Superuser role; adding an already-authored snippet or template to a device only needs Read-Write. See Who can attach an authentication policy.

Certificates and CAs (TRUST_CA / TRUST_CERT)

Section titled “Certificates and CAs (TRUST_CA / TRUST_CERT)”

The full guide is Certificates. A few things specific to authoring these two types from the command line:

  • Prerequisites: agent 1.21.0 or later, OPNsense 26.1.11 or later, an unencrypted private key for each TRUST_CERT, ndcli 1.37.0 or later, and the device’s Allow All Snippet Content setting turned on — it’s off by default (see Device-Side Protection).

  • Superuser, interactively. Creating, updating, renaming, reprioritizing or deleting either type needs the Superuser role from ndcli auth login; it’s refused under NDCLI_TOKEN, because a personal access token is capped at Read-Write.

  • Wrap the PEM with jq. crt holds the certificate’s line breaks escaped as JSON. jq -Rs reads a PEM file raw, as one string; single-quote the program so your shell leaves ${...} alone:

    Terminal window
    jq -Rs '{name: "fw-wildcard", crt: ., key: "${fw_wildcard_key}"}' fw-wildcard.crt > fw-wildcard.json
    ndcli snippet create fw-wildcard --type TRUST_CERT --file fw-wildcard.json
  • Put the key in with --value-stdin. key names a secret organization variable. The no-echo prompt reads a single line, so it can’t take a PEM key — redirect the file instead:

    Terminal window
    ndcli variable org create fw_wildcard_key --secret --value-stdin < ./fw-wildcard.key
  • Renew with update-content. Rebuild the content with the same jq recipe and run snippet update-content. Leaving uuid out is fine; NetDefense keeps the one it assigned. When the key changes too, update its variable right before or after — see Renewing a certificate.

USER, GROUP, ZABBIX_USERPARAMETER, and ZABBIX_SETTINGS content that can give a firewall account administrator rights, or run commands on the device, needs a Superuser — see Snippets that grant administrator rights for exactly what counts, and for the errors a sync reports when it’s missing. From the command line:

  • Sign in interactively. Saving such content — snippet create, snippet update-content, snippet edit, or a snippet pull with --auto-create or --overwrite — needs a Superuser signed in with ndcli auth login. It’s refused under NDCLI_TOKEN, because a personal access token never carries Superuser clearance.
  • Re-save with update-content. To give an existing snippet Superuser clearance, save its current content again with snippet update-content. snippet edit sends nothing when the content is unchanged.
  • Pulls are checked too. See Superuser-level content for the codes a refused pull reports.
CommandDescription
snippet listList snippets
snippet describeShow snippet details
snippet createCreate a new snippet
snippet deleteDelete a snippet
snippet editEdit snippet in external editor
snippet pullPull snippet from a device
snippet renameRename a snippet
snippet set-prioritySet snippet priority
snippet update-contentUpdate snippet content from file

List snippets in your organization.

Terminal window
ndcli snippet list [flags]
FlagTypeDefaultDescription
--namestringFilter by name (regex pattern)
--typestringFilter by type: USER, GROUP, ALIAS, RULE, UNBOUND_HOST_OVERRIDE, UNBOUND_DOMAIN_FORWARD, UNBOUND_HOST_ALIAS, UNBOUND_ACL, ZABBIX_SETTINGS, ZABBIX_USERPARAMETER, ZABBIX_ALIAS, AUTH_SERVER, AUTH_ORDER, TRUST_CA, TRUST_CERT
--created-afterstringFilter by created date
--created-beforestringFilter by created date
--updated-afterstringFilter by updated date
--updated-beforestringFilter by updated date
--sort-bystringpriority:ascSort field: priority, name, created_at, updated_at
--pageint1Page number
--per-pageint50Items per page (max 100)
Terminal window
ndcli snippet list
ndcli snippet list --type RULE
ndcli snippet list --type ALIAS
ndcli snippet list --name "admin.*"
ndcli snippet list --updated-after 24h
ndcli snippet list --sort-by name:asc

Show detailed information about a snippet including its content.

Terminal window
ndcli snippet describe [name]
ArgumentRequiredDescription
nameYesSnippet name

Shows:

  • Snippet name and type
  • Priority
  • Full content
  • Creation and update timestamps

With -f json, describe prints one JSON document: the snippet object, with its content as the content string.

Terminal window
ndcli snippet describe admin-users
ndcli snippet describe firewall-rules -f json

Create a new snippet.

Terminal window
ndcli snippet create [name] [flags]
ArgumentRequiredDescription
nameYesSnippet name
FlagTypeDefaultDescription
--typestringRequiredSnippet type: USER, GROUP, ALIAS, RULE, UNBOUND_HOST_OVERRIDE, UNBOUND_DOMAIN_FORWARD, UNBOUND_HOST_ALIAS, UNBOUND_ACL, ZABBIX_SETTINGS, ZABBIX_USERPARAMETER, ZABBIX_ALIAS, AUTH_SERVER, AUTH_ORDER, TRUST_CA, TRUST_CERT
--contentstringSnippet content (inline)
--filestringRead content from file
--priorityint1000Priority (1-60000, lower = higher priority)
Terminal window
ndcli snippet create web-servers \
--type ALIAS \
--content '{"name": "web_servers", "type": "host", "content": ["192.168.1.10", "192.168.1.11"]}'
ndcli snippet create firewall-rules \
--type RULE \
--file ./rules.conf
ndcli snippet create critical-rules \
--type RULE \
--file ./critical.conf \
--priority 100

Priority orders firewall rules inside each OPNsense rule section: PREPEND rules before APPEND rules, each with lower numbers first — see Policy Chain. Suggested ranges:

RangeSuggested Use
1-500Critical/foundational rules
500-1000Core infrastructure
1000-2000Standard configurations
2000-5000Application-specific rules
5000+Optional/override rules

Delete a snippet.

Terminal window
ndcli snippet delete [name]
ArgumentRequiredDescription
nameYesSnippet name to delete
Terminal window
ndcli snippet delete deprecated-rules

Open a snippet’s content in your system’s default text editor for editing.

Terminal window
ndcli snippet edit [name]
ArgumentRequiredDescription
nameYesSnippet name
  1. Downloads current snippet content to a temporary file
  2. Opens the file in your default editor ($EDITOR or system default)
  3. After you save and close, uploads the modified content
  4. If no changes were made, no update occurs

Because nothing is sent when the content is unchanged, snippet edit can’t re-save a snippet to give it Superuser clearance — use snippet update-content for that.

VariableDescription
EDITORPreferred text editor (e.g., vim, nano, code)
VISUALAlternative editor variable
Terminal window
ndcli snippet edit firewall-rules
EDITOR=nano ndcli snippet edit firewall-rules

Pull configuration content from a device and optionally save it as a snippet.

Terminal window
ndcli snippet pull [device] [match-key] [flags]

Pull copies an existing object’s configuration so you can base a new snippet on it. It does not place the object it read under NetDefense management, and the resulting snippet keeps no link back to it — see Portable Snippets and the PULL Workflow for the full workflow, including what happens if you later sync the snippet to the same device you pulled from.

From agent 1.23.0, a pulled RULE or ALIAS is portable snippet content: the fields that say what it does and every other field that differs from OPNsense’s default, as strings, with categories and overload tables named and NetDefense’s template tags removed from the description — see RULE and ALIAS fields. A rule OPNsense generates itself can’t be pulled, and neither can a legacy rule until it’s migrated in OPNsense.

Review what comes back before reusing it — a pulled snippet carries whatever the source device’s configuration held, secrets included. (From ndagent 1.20.1, a USER account’s password is the exception: pull doesn’t copy it — see USER snippets and passwords.) Move anything sensitive into a secret variable before assigning the snippet to a template or sharing it.

ArgumentRequiredDescription
deviceYesDevice name to pull from
match-keyYesWhat identifies the object on the device — see match semantics. May contain any characters, spaces included
FlagTypeDefaultDescription
--typestringALIASConfig type to pull (see match semantics below): USER, GROUP, ALIAS, RULE, UNBOUND_HOST_OVERRIDE, UNBOUND_DOMAIN_FORWARD, UNBOUND_HOST_ALIAS, UNBOUND_ACL, ZABBIX_SETTINGS, ZABBIX_USERPARAMETER, ZABBIX_ALIAS
--namestringderivedName for the created snippet: letters, digits, dot, underscore and hyphen, up to 255 characters
--auto-createboolfalseCreate snippet in DB if it doesn’t exist
--overwriteboolfalseUpdate snippet if it already exists
--wait-wfalseWait for task to complete

AUTH_SERVER and AUTH_ORDER are deliberately absent from --type here — they can’t be pulled. OPNsense has no read API for authentication servers or the login order for the agent to read back from, and pulling a server would mean copying its bind password off the device. Author these two types by hand instead — see Authentication Servers. TRUST_CA and TRUST_CERT are absent too: pulling a certificate would copy its private key off the device in the clear. See Certificates.

The positional argument matches the object on the device; --name names the snippet made from it. The two have different rules, which is why they are separate: a match key may contain anything — rule descriptions almost always contain spaces — while a snippet name may only contain letters, digits, dot, underscore and hyphen.

Without --name, the snippet name is derived from the match key: lowercased, with every run of characters outside that set collapsed to a single hyphen, and any leading or trailing hyphens trimmed. So a rule matched by Allow HTTPS from LAN to DMZ becomes the snippet allow-https-from-lan-to-dmz.

Pass --name when the derived name is not what you want:

Terminal window
ndcli snippet pull fw-prod-01 "Allow HTTPS from LAN to DMZ" \
--type RULE --name allow-https-lan-dmz --auto-create --wait

A match key with nothing to derive from — punctuation only — fails and asks for --name, rather than creating a snippet with an empty or meaningless name.

Name collisions behave as they always have: with --auto-create alone an existing snippet name is refused, and with --overwrite it is updated in place.

Terminal window
ndcli snippet pull fw-prod-01 production-aliases --type ALIAS
ndcli snippet pull fw-prod-01 production-aliases --type ALIAS --wait
ndcli snippet pull fw-prod-01 new-rules \
--type RULE \
--auto-create \
--wait
ndcli snippet pull fw-prod-01 existing-aliases \
--type ALIAS \
--overwrite \
--wait

The snippet-name argument doubles as the lookup key on the device. How it’s interpreted depends on --type:

TypeLookup
USERexact username. From ndagent 1.20.1, the account’s password is not copied — see USER snippets and passwords
GROUPexact group name
ALIASexact alias name
RULEpartial description match (case-insensitive); fails if multiple match, or if the match is a legacy rule or one OPNsense generates
UNBOUND_HOST_OVERRIDEhostname.domain (e.g. server1.local)
UNBOUND_DOMAIN_FORWARDdomain name (e.g. internal.corp)
UNBOUND_HOST_ALIAShostname.domain (e.g. www.local)
UNBOUND_ACLACL name (e.g. lan-clients)
ZABBIX_SETTINGSsingleton — there is nothing to match on the device, so the match key only feeds the derived snippet name; the agent returns the full Zabbix Agent settings tree
ZABBIX_USERPARAMETERexact UserParameter key (e.g. nd-cpu-temp)
ZABBIX_ALIASexact item-alias key (e.g. nd-uname)

A pull that saves a snippet (--auto-create or --overwrite) is held to the same rules as saving the snippet yourself — see Snippets that grant administrator rights. When one is refused, the pull task fails, nothing is saved, and the task message starts with one of these codes:

CodeRefused whenApplies to
PULL_SUPERUSER_REQUIREDThe content is elevated — for example a USER or GROUP with an administrator-equivalent privilege, an account or group the device reports as administrator-equivalent, or a Zabbix command, remote commands, or sudoRead-Write members and every personal access token
PULL_GROUPS_UNVERIFIEDA USER that belongs to any group, pulled from a device whose agent is older than 1.20.1 and doesn’t report whether those groups grant administrator rightsRead-Write members and every personal access token
PULL_OVERWRITE_SUPERUSER_REQUIRED--overwrite would replace a snippet that holds elevated content, or a USER or GROUP snippet a Superuser last savedRead-Write members and every personal access token
PULL_PROTECTED_USERThe account is root, netdefense-agent, or netdefense-readonlyEveryone
PULL_PROTECTED_GROUPThe group is admins or netdefense-readonlyEveryone
PULL_CONTENT_UNCLASSIFIABLEThe content can’t be classified or has no usable name, so no device could apply itEveryone

A pull without --auto-create or --overwrite only previews the content, and none of these apply. A saving pull run by a Superuser stores the snippet with Superuser clearance.

By default, snippet pull creates an asynchronous task and returns immediately with a task ID. Use --wait to block until completion, or check task status with:

Terminal window
ndcli task describe <task-id>

With -f json, snippet pull prints one JSON document on standard output: the task (task, status, config_type, snippet) and, once --wait sees it complete, the pulled content. The --wait progress lines go to standard error.

{
"task": "abc12345",
"status": "COMPLETED",
"config_type": "ALIAS",
"snippet": "corp",
"content": "{\"content\": \"10.0.0.1\", \"description\": \"\", \"enabled\": \"1\", \"name\": \"corp\", \"type\": \"host\", \"uuid\": \"221f3268-4ac9-4cb3-aad3-63a0f8c09585\"}"
}

Rename a snippet.

Terminal window
ndcli snippet rename [name] [new-name]
ArgumentRequiredDescription
nameYesCurrent snippet name
new-nameYesNew snippet name
Terminal window
ndcli snippet rename old-rules new-rules

Change a snippet’s priority.

Terminal window
ndcli snippet set-priority [name] [priority]
ArgumentRequiredDescription
nameYesSnippet name
priorityYesNew priority (1-60000)
Terminal window
ndcli snippet set-priority critical-rules 100
ndcli snippet set-priority optional-rules 5000

Update a snippet’s content from a file.

Terminal window
ndcli snippet update-content [name] [file]
ArgumentRequiredDescription
nameYesSnippet name
fileYesPath to file containing new content

The file’s content is saved even when it matches what’s already stored. That makes update-content the way for a Superuser to give an existing snippet Superuser clearance: save the content snippet describe prints under Content to a file, and pass that file back.

Terminal window
ndcli snippet update-content firewall-rules ./updated-rules.conf
cat new-content.conf | ndcli snippet update-content my-snippet /dev/stdin
# Re-save unchanged content, signed in as a Superuser, to record Superuser clearance
ndcli snippet update-content svc-backup ./svc-backup.json