Device identity¶
Before handing a customer's administration credentials to whoever is on the other end, the platform checks who it is talking to. That holds for both access paths: an API's TLS certificate, and the SSH host key.
Why this is not the usual verification¶
A network device rarely presents a certificate or a host key attested by a public
authority: in its factory configuration, it carries its own. A FortiGate presents
CN=FortiGate, issued by an authority private to the device
(CN=<serial number>, OU=Certificate Authority) that nobody else knows. PAN-OS
and BIG-IP are in the same position.
The question is therefore not only “is the chain valid” — most often it is not —
but “have I seen this fingerprint before, and did I accept it”. This is the
known_hosts trust model, held at the level of the service rather than on each
agent: a reinstalled agent has nothing to learn again.
A fleet equipped with an internal authority is still served by the “chain verified” posture: the case exists, and it is exactly the one it serves.
Postures¶
Three possible ways to behave in front of an unknown fingerprint, adjustable global → customer → device, field by field and separately for TLS and SSH — exactly like retention.
| Posture | In front of an unknown fingerprint |
|---|---|
| Manual approval (default) | the connection is suspended, the fingerprint is put on hold; the run fails saying so |
| Trust on first use | the first fingerprint met is accepted and remembered without asking |
| Always refuse | only fingerprints already approved are accepted; nothing is put on hold |
Manual approval is the only defensible default: accepting outright would amount to trusting whoever turns up first, refusing outright would make the platform unusable before any configuration.
Which posture to choose. To take over an existing fleet, trust on first use saves approving one fingerprint per device: the first connection remembers, and any later change goes back on hold all the same. For a fleet in steady state, manual approval turns each new fingerprint into an explicit, logged decision. Always refuse suits a frozen perimeter where anything new must be a deliberate act.
A change is never a first encounter¶
When an approved fingerprint already exists and a different one presents itself, no posture accepts it silently — not even “trust on first use”.
This is the one case that separates a legitimately renewed certificate from an interception, and it demands a human decision. The approval screen shows the old and the new one side by side so the matter can be settled.
Under “always refuse”, a change is recorded as rejected without being put on hold: accepting it means changing posture, which is a deliberate act and itself logged.
Pending fingerprints¶
The dashboard flags pending fingerprints. A device stuck there is not backed up — it is an operational exception in the same way a failure is.
A run's failure message gives the fingerprint presented:
SSH host key of 192.168.0.142 not approved — fingerprint presented
SHA256:NRGPQi0x0+4jwOOU+MjxCRNtMtfeYk0X5MZyPexsIiM. it is on hold: approve it
on the device page, then run again.
Approving¶
From the device page, Fingerprints section: compare the fingerprint presented with the one the device itself displays, then approve or reject.
How to check from the other side:
| Path | Format | Where to read it |
|---|---|---|
| SSH | SHA256:<base64> |
ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub on the device, or what ssh displays on a first connection |
| TLS | hexadecimal, AB:CD:… |
the device's administration interface, a browser, or openssl x509 -noout -fingerprint -sha256 |
This is OpenSSH's format for SSH, and the usual browser format for TLS: in both cases the string compares visually with what the device shows, with no conversion.
Also via the API.
Approving or rejecting requires# Pending fingerprints, all devices curl -s "$BASE/api/trust/fingerprints?pending_only=true&tenant=$CUSTOMER" \ -H "Authorization: Bearer $TOKEN" # The fingerprints of one given device curl -s "$BASE/api/trust/fingerprints?device_id=$DEVICE&tenant=$CUSTOMER" \ -H "Authorization: Bearer $TOKEN" # Approve curl -s -X POST "$BASE/api/trust/fingerprints/$FINGERPRINT/approve?tenant=$CUSTOMER" \ -H "Authorization: Bearer $TOKEN" # Reject curl -s -X POST "$BASE/api/trust/fingerprints/$FINGERPRINT/reject?tenant=$CUSTOMER" \ -H "Authorization: Bearer $TOKEN"credential:write. That is the same scope as declaring a credential: approving a fingerprint amounts to naming the party to which the platform will hand the customer's administration credentials.
Approving a replacement fingerprint withdraws the previous one: without that, a revoked certificate would stay accepted indefinitely.
Every decision is logged with the fingerprint, the access path and the device.
A refusal never triggers a fallback¶
An unapproved fingerprint fails the run; it does not switch to the other transport. Moving from the API to SSH because the certificate is unknown would amount to working around the policy instead of applying it.
Pinned certificate¶
Independently of the cascade, a device can carry the SHA-256 fingerprint of the expected certificate, entered on its page (API connection section → TLS verification → Certificate pinned).
Pinning is not a verification after the fact: the matching certificate is loaded as the session's sole trust authority. A substitution between reading the fingerprint and the application exchange therefore fails the handshake — it does not go unnoticed.
Three possible postures for a device's TLS verification:
| Setting | Effect |
|---|---|
| Chain verified | validation against the system authorities — suits a device whose certificate comes from your internal authority |
| Certificate pinned | a real check on that precise certificate, without demanding a PKI overhaul |
| Disabled | traffic encrypted, but no identity guarantee at all — flagged by a pill on the device page |
Pinning takes precedence over the cascade: it is an operator's explicit decision, stronger than a general posture.
Obtaining the fingerprint to pin¶
There is nothing to go and fetch: run Test the connection on a device whose certificate is not validated, and the message gives the fingerprint presented, ready to copy straight into the pinning field.
the certificate of 172.16.0.4 is not validated by the system trust store
(self-signed?). SHA-256 fingerprint presented: D7:D5:51:97:0B:60:7B:E8:… — pin
it on the device to get a real check, or disable TLS verification
If the pinned fingerprint does not match the one presented, the message states both:
unexpected certificate on 172.16.0.4:443 — fingerprint presented D7:D5:…,
pinned fingerprint AB:AB:…. If the device certificate was legitimately renewed,
update the pinned fingerprint; otherwise, do not connect.
Setting the posture¶
Interface: Settings for the global level, a customer's page for its level,
a device's configuration panel for its own. Each screen displays the origin
of the effective value — global, tenant or device — as retention does.
Also via the API.
A field set to# Effective posture, with the origin of each field curl -s "$BASE/api/trust/effective?tenant=$CUSTOMER&device_id=$DEVICE" \ -H "Authorization: Bearer $TOKEN" # The available postures and what each one implies curl -s "$BASE/api/trust/postures" -H "Authorization: Bearer $TOKEN" # Set a posture at customer level curl -s -X PUT "$BASE/api/trust/tenant?tenant=$CUSTOMER" \ -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \ -d '{"posture_ssh":"confiance_au_premier_contact","posture_tls":null}'nullinherits from the level above. The accepted values aremanuelle,confiance_au_premier_contactandrefus— “manual approval”, “trust on first use” and “always refuse”. Changing the posture requiresretention:write: the same right as retention, both settings deciding how the platform behaves across a customer's fleet.
Session binding¶
Once the fingerprint is approved, the session is bound to it. For SSH, the
approved key is written to an ephemeral known_hosts file, in memory
(/dev/shm, permissions 0600), passed to the SSH library in strict mode and to
rsync with StrictHostKeyChecking=yes.
In other words, the verification does not merely precede the connection: it constrains it. There is no return to blind trust after the decision.