Ship a Korthveil firewall to a site, plug it in, and it sets itself up with no one at the site typing anything. It provisions itself from a document your organization signs. The document gives the firewall its management port, host name and administrator. It can also enroll the firewall in Korthveil Central or SentinelOps, which then send the rest of the configuration.
You need:
korthveil-ztp tool (built from the Korthveil sources: go build ./cmd/korthveil-ztp) on an administrator's computer; your office the site
┌──────────────────────────┐ ┌────────────────────────────────┐
│ org.key (kept offline) │ ztp.json │ unclaimed Korthveil firewall │
│ korthveil-ztp sign ──────┼──────────────────▶│ trusts org.pem │
└──────────────────────────┘ USB stick or │ checks the signature, device, │
HTTPS via DNS │ and expiry, then commits it │
└────────────────────────────────┘
When it applies: only while the firewall is unclaimed, meaning freshly installed or factory-reset, with no administrator and no configuration of its own. Once set up, it never looks for documents again.
What is trusted: only the signature. A document signed with another key, meant for another device, or expired is ignored and logged. Whoever controls the site's DHCP or DNS cannot take a firewall over.
korthveil-ztp keygen -out org
This writes:
org.key: the private key. Keep it offline, like a CA key; it can provision your firewalls.org.pem: the public key, which the firewalls trust.Pick one:
make platform ZTP_TRUST=/absolute/path/to/org.pem, then make usb. Every firewall installed from that image trusts the key.korthveil-installer answers --disk serial:<serial> --erase --zero-touch-trust-file org.pem. With no --mgmt, the firewall is installed unclaimed.The key the installer stored survives a factory reset: a reset firewall provisions itself again.
korthveil-ztp template > doc.json
echo 'the administrator password' | korthveil-ztp hash # paste into seed.admin.passwordHash
korthveil-ztp sign -key org.key doc.json > ztp.json
korthveil-ztp verify -trust org.pem -serial G6JY1234 ztp.json
The document's fields:
| Field | Meaning |
|---|---|
devices |
serial:<system serial> or mac:<address> of any port. Leave it empty for any of your firewalls. |
notAfter |
When the document expires; at most 400 days ahead. |
seed.hostname |
The firewall's name. |
seed.management |
The management port: interface (e.g. enp1s0), or mac, or neither when the document comes through DNS (the port it arrived on is used). Then ipv4Mode dhcp or static (ipv4, gateway4), and ipv6Mode slaac, static, or none. |
seed.admin |
name and passwordHash (from korthveil-ztp hash). Without it, the console's setup code stays the way to create the administrator. |
seed.modules |
Add-ins to turn on. Use central-management (with its enrollment token) or sentinelops to enroll; they then deliver the full configuration. |
seed.zones, rules, sourceNat, portForwards, interfaces, dns, sshKeys |
Optional first policy, the same as in an answers file. |
Enrollment tokens in the document are secrets:
USB stick (simplest):
KV-ZTP (FAT labels hold at most 11 characters).ztp.json to it.The firewall reads the stick read-only. It is safe to leave it in.
DNS and HTTPS (no one on site):
ztp.json on an HTTPS server with a publicly trusted certificate._korthveil-ztp.branch17.example.com. TXT "v=kztp1 url=https://ztp.example.com/fw/G6JY1234.json"
While unclaimed, the firewall runs DHCPv4 and SLAAC on every port. Only the web UI and SSH answer, so someone on site can still set it up by hand with the setup code shown on the console. Nothing is forwarded. It learns DNS servers and the search domain from DHCP or router advertisements (RDNSS/DNSSL), looks up the record, and fetches the document. IPv4-only, IPv6-only, and dual-stack sites all work.
At the firewall: the console shows Zero-touch provisioning: looking for a USB stick labelled KV-ZTP or DNS _korthveil-ztp.\dns:<domain>).
In Central or SentinelOps: the firewall appears as soon as it enrolls.
| Symptom | Check |
|---|---|
| Nothing happens | The firewall must trust a key (step 2) and be unclaimed. A firewall with an administrator never provisions itself. |
| document ignored: not signed by a trusted key (log) | It was signed with another org.key, or the firewall trusts another org.pem. |
| the document is for other devices | The serial: must match the system serial (dmidecode -s system-serial-number), or a mac: must match a port. |
| the document has expired | Sign it again with a later notAfter. |
| The USB stick is not read | The label must be exactly KV-ZTP, the file system FAT or exFAT, and the file ztp.json at the top, under 1 MiB. |
| DNS discovery does not start | The site's DHCP (option 15 or 119) or router advertisements (DNSSL) must give the domain, and the TXT record must be at _korthveil-ztp.<that domain>. |
More: adr/0062 describes the design and its security.