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 Types
Section titled “Snippet Types”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.
| Type | Description |
|---|---|
USER | User definition (OPNsense auth/user). password is the plaintext to set, never a hash — see USER snippets and passwords |
GROUP | Group definition (OPNsense auth/group) |
ALIAS | Network alias — host, network, or port set (OPNsense firewall/alias) |
RULE | Firewall rule (OPNsense firewall/filter) |
UNBOUND_HOST_OVERRIDE | Unbound DNS host override — A/AAAA/MX/TXT record |
UNBOUND_DOMAIN_FORWARD | Unbound DNS domain forwarding (incl. DoT) |
UNBOUND_HOST_ALIAS | Unbound DNS host alias (CNAME-style, references a host override) |
UNBOUND_ACL | Unbound DNS query ACL |
ZABBIX_SETTINGS | Zabbix Agent global settings — singleton; the agent owns the full local/main/tuning/features tree on every apply |
ZABBIX_USERPARAMETER | Zabbix UserParameter (custom check command). Key must start with nd- |
ZABBIX_ALIAS | Zabbix item-key alias. Key must start with nd- |
AUTH_SERVER | External LDAP or Active Directory authentication server. See Authentication Servers. |
AUTH_ORDER | Login-order policy for a facility (webadmin) — which authentication sources are tried, and in what sequence. See Authentication Servers. |
TRUST_CA | CA certificate, without its private key, synced into the device’s trust store (OPNsense trust/ca). See Certificates. |
TRUST_CERT | Certificate and its private key, which comes from a secret variable, synced into the device’s trust store (OPNsense trust/cert). See Certificates. |
Required Content Fields
Section titled “Required Content Fields”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.
| Type | Required fields |
|---|---|
ALIAS | name, type |
RULE | action, non-empty |
UNBOUND_HOST_OVERRIDE | hostname, domain |
UNBOUND_DOMAIN_FORWARD | domain, server |
UNBOUND_HOST_ALIAS | hostname, domain |
UNBOUND_ACL | name, action |
ZABBIX_ALIAS | key |
USER, GROUP, ZABBIX_USERPARAMETER, ZABBIX_SETTINGS | as described for each type above |
AUTH_SERVER | name, type, host, ldap_port, ldap_urltype, ldap_protver, ldap_scope, ldap_basedn, ldap_authcn, ldap_attr_user |
AUTH_ORDER | facilities |
TRUST_CA | name, crt |
TRUST_CERT | name, crt, key |
Everything else in a snippet’s content is optional. Beyond the required fields:
RULEandALIAScontent 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’saction, 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 withSNIPPET_AGENT_TOO_OLDwhen a snippet uses a field that agent would drop — see Agent and OPNsense versions.RULE,ALIASandUNBOUND_*values are JSON strings in OPNsense’s own form:"443","1","lan,wan".enabled, and a domain forward’sforward_tcp_upstreamandforward_first, may also betrueorfalse, and an alias’scontentand an ACL’snetworksa list of strings. Any other value,nullincluded, is refused on create and update, naming the field and the form to write — see Field values.AUTH_SERVER,AUTH_ORDER,TRUST_CAandTRUST_CERTaccept only a closed set of known keys (an unknown key is rejected) and require strict JSON with no duplicate keys.AUTH_SERVERandAUTH_ORDERalso reject auuidorrefidsupplied by hand, and ignore--priorityentirely.TRUST_CAandTRUST_CERTreject auuidon 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.
USER snippets and passwords
Section titled “USER snippets and passwords”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
USERsnippet for an account that doesn’t exist on the device yet fails without one. - Omit
passwordto keep an existing account’s password. When the account is already on the device, a snippet withoutpasswordleaves its current password alone. - A pulled snippet has no password. From ndagent 1.20.1,
snippet pulldoesn’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 apasswordbefore you apply the snippet to a device where the account doesn’t exist yet. - A hash is refused. Saving a
passwordwith 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 codeUSER_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$namethemselves beforendcliever sees it.
# Store the password once, keeping it off the command linendcli variable org create svc_monitoring_password --secret --value-stdin < ./password.txt
# Reference it from the snippetndcli 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_namethemselves beforendcliever sees it. Single-quote inline--content, or put the JSON in a file with--fileinstead. Local Databasemust be first. EveryAUTH_ORDERsnippet defining thewebadminfacility must listLocal Databaseasorder[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 Databaseis 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_ORDERsnippet may still name one; check it yourself first. - Cleartext (
"ldap_urltype": "TCP - Standard") needs an explicit"nd_allow_cleartext_ldap": truein the content, or the create is refused. - Attaching authored content is a lower bar than authoring it. Creating or updating
AUTH_SERVER/AUTH_ORDERcontent 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 underNDCLI_TOKEN, because a personal access token is capped at Read-Write. -
Wrap the PEM with
jq.crtholds the certificate’s line breaks escaped as JSON.jq -Rsreads 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.jsonndcli snippet create fw-wildcard --type TRUST_CERT --file fw-wildcard.json -
Put the key in with
--value-stdin.keynames 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 samejqrecipe and runsnippet update-content. Leavinguuidout is fine; NetDefense keeps the one it assigned. When the key changes too, update its variable right before or after — see Renewing a certificate.
Snippets that grant administrator rights
Section titled “Snippets that grant administrator rights”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 asnippet pullwith--auto-createor--overwrite— needs a Superuser signed in withndcli auth login. It’s refused underNDCLI_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 withsnippet update-content.snippet editsends nothing when the content is unchanged. - Pulls are checked too. See Superuser-level content for the codes a refused pull reports.
Commands
Section titled “Commands”| Command | Description |
|---|---|
snippet list | List snippets |
snippet describe | Show snippet details |
snippet create | Create a new snippet |
snippet delete | Delete a snippet |
snippet edit | Edit snippet in external editor |
snippet pull | Pull snippet from a device |
snippet rename | Rename a snippet |
snippet set-priority | Set snippet priority |
snippet update-content | Update snippet content from file |
snippet list
Section titled “snippet list”List snippets in your organization.
ndcli snippet list [flags]| Flag | Type | Default | Description |
|---|---|---|---|
--name | string | Filter by name (regex pattern) | |
--type | string | Filter 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-after | string | Filter by created date | |
--created-before | string | Filter by created date | |
--updated-after | string | Filter by updated date | |
--updated-before | string | Filter by updated date | |
--sort-by | string | priority:asc | Sort field: priority, name, created_at, updated_at |
--page | int | 1 | Page number |
--per-page | int | 50 | Items per page (max 100) |
Examples
Section titled “Examples”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:ascsnippet describe
Section titled “snippet describe”Show detailed information about a snippet including its content.
ndcli snippet describe [name]Arguments
Section titled “Arguments”| Argument | Required | Description |
|---|---|---|
name | Yes | Snippet name |
Output
Section titled “Output”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.
Examples
Section titled “Examples”ndcli snippet describe admin-usersndcli snippet describe firewall-rules -f jsonsnippet create
Section titled “snippet create”Create a new snippet.
ndcli snippet create [name] [flags]Arguments
Section titled “Arguments”| Argument | Required | Description |
|---|---|---|
name | Yes | Snippet name |
| Flag | Type | Default | Description |
|---|---|---|---|
--type | string | Required | Snippet 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 |
--content | string | Snippet content (inline) | |
--file | string | Read content from file | |
--priority | int | 1000 | Priority (1-60000, lower = higher priority) |
Examples
Section titled “Examples”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 100Priority Guidelines
Section titled “Priority Guidelines”Priority orders firewall rules inside each OPNsense rule section: PREPEND rules before APPEND rules, each with lower numbers first — see Policy Chain. Suggested ranges:
| Range | Suggested Use |
|---|---|
| 1-500 | Critical/foundational rules |
| 500-1000 | Core infrastructure |
| 1000-2000 | Standard configurations |
| 2000-5000 | Application-specific rules |
| 5000+ | Optional/override rules |
snippet delete
Section titled “snippet delete”Delete a snippet.
ndcli snippet delete [name]Arguments
Section titled “Arguments”| Argument | Required | Description |
|---|---|---|
name | Yes | Snippet name to delete |
Examples
Section titled “Examples”ndcli snippet delete deprecated-rulessnippet edit
Section titled “snippet edit”Open a snippet’s content in your system’s default text editor for editing.
ndcli snippet edit [name]Arguments
Section titled “Arguments”| Argument | Required | Description |
|---|---|---|
name | Yes | Snippet name |
How It Works
Section titled “How It Works”- Downloads current snippet content to a temporary file
- Opens the file in your default editor (
$EDITORor system default) - After you save and close, uploads the modified content
- 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.
Environment Variables
Section titled “Environment Variables”| Variable | Description |
|---|---|
EDITOR | Preferred text editor (e.g., vim, nano, code) |
VISUAL | Alternative editor variable |
Examples
Section titled “Examples”ndcli snippet edit firewall-rules
EDITOR=nano ndcli snippet edit firewall-rulessnippet pull
Section titled “snippet pull”Pull configuration content from a device and optionally save it as a snippet.
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.
Arguments
Section titled “Arguments”| Argument | Required | Description |
|---|---|---|
device | Yes | Device name to pull from |
match-key | Yes | What identifies the object on the device — see match semantics. May contain any characters, spaces included |
| Flag | Type | Default | Description |
|---|---|---|---|
--type | string | ALIAS | Config 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 |
--name | string | derived | Name for the created snippet: letters, digits, dot, underscore and hyphen, up to 255 characters |
--auto-create | bool | false | Create snippet in DB if it doesn’t exist |
--overwrite | bool | false | Update snippet if it already exists |
--wait | -w | false | Wait 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.
Naming the created snippet
Section titled “Naming the created snippet”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:
ndcli snippet pull fw-prod-01 "Allow HTTPS from LAN to DMZ" \ --type RULE --name allow-https-lan-dmz --auto-create --waitA 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.
Examples
Section titled “Examples”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 \ --waitMatch Semantics
Section titled “Match Semantics”The snippet-name argument doubles as the lookup key on the device. How it’s
interpreted depends on --type:
| Type | Lookup |
|---|---|
USER | exact username. From ndagent 1.20.1, the account’s password is not copied — see USER snippets and passwords |
GROUP | exact group name |
ALIAS | exact alias name |
RULE | partial description match (case-insensitive); fails if multiple match, or if the match is a legacy rule or one OPNsense generates |
UNBOUND_HOST_OVERRIDE | hostname.domain (e.g. server1.local) |
UNBOUND_DOMAIN_FORWARD | domain name (e.g. internal.corp) |
UNBOUND_HOST_ALIAS | hostname.domain (e.g. www.local) |
UNBOUND_ACL | ACL name (e.g. lan-clients) |
ZABBIX_SETTINGS | singleton — 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_USERPARAMETER | exact UserParameter key (e.g. nd-cpu-temp) |
ZABBIX_ALIAS | exact item-alias key (e.g. nd-uname) |
Superuser-level content
Section titled “Superuser-level content”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:
| Code | Refused when | Applies to |
|---|---|---|
PULL_SUPERUSER_REQUIRED | The 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 sudo | Read-Write members and every personal access token |
PULL_GROUPS_UNVERIFIED | A 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 rights | Read-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 saved | Read-Write members and every personal access token |
PULL_PROTECTED_USER | The account is root, netdefense-agent, or netdefense-readonly | Everyone |
PULL_PROTECTED_GROUP | The group is admins or netdefense-readonly | Everyone |
PULL_CONTENT_UNCLASSIFIABLE | The content can’t be classified or has no usable name, so no device could apply it | Everyone |
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.
Async Behavior
Section titled “Async Behavior”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:
ndcli task describe <task-id>JSON output
Section titled “JSON output”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\"}"}snippet rename
Section titled “snippet rename”Rename a snippet.
ndcli snippet rename [name] [new-name]Arguments
Section titled “Arguments”| Argument | Required | Description |
|---|---|---|
name | Yes | Current snippet name |
new-name | Yes | New snippet name |
Examples
Section titled “Examples”ndcli snippet rename old-rules new-rulessnippet set-priority
Section titled “snippet set-priority”Change a snippet’s priority.
ndcli snippet set-priority [name] [priority]Arguments
Section titled “Arguments”| Argument | Required | Description |
|---|---|---|
name | Yes | Snippet name |
priority | Yes | New priority (1-60000) |
Examples
Section titled “Examples”ndcli snippet set-priority critical-rules 100
ndcli snippet set-priority optional-rules 5000snippet update-content
Section titled “snippet update-content”Update a snippet’s content from a file.
ndcli snippet update-content [name] [file]Arguments
Section titled “Arguments”| Argument | Required | Description |
|---|---|---|
name | Yes | Snippet name |
file | Yes | Path 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.
Examples
Section titled “Examples”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 clearancendcli snippet update-content svc-backup ./svc-backup.json