netbirdio/netbird · critical
revocation list verification failed
Error message
revocation list verification failed
What it means
Returned by the revocation-list verification path (client/internal/updater/reposign/revocation.go:150) when verifyAny fails to verify the reconstructed signed message (revocation list data || little-endian timestamp) against any trusted public root key. The revocation list is what rejects compromised updater keys, so an unverifiable list is rejected outright rather than treated as empty; this check fails closed by design.
Source
Thrown at client/internal/updater/reposign/revocation.go:150
}
// Validate signature timestamp is close to LastUpdated
// (prevents signing old lists with new timestamps)
timeDiff := signature.Timestamp.Sub(revoList.LastUpdated).Abs()
if timeDiff > maxClockSkew {
err := fmt.Errorf("signature timestamp %v differs too much from list LastUpdated %v (diff: %v)",
signature.Timestamp, revoList.LastUpdated, timeDiff)
log.Errorf("timestamp mismatch in revocation list: %v", err)
return nil, err
}
// Reconstruct the signed message: revocation_list_data || timestamp || version
msg := make([]byte, 0, len(data)+8)
msg = append(msg, data...)
msg = binary.LittleEndian.AppendUint64(msg, uint64(signature.Timestamp.Unix()))
if !verifyAny(publicRootKeys, msg, signature.Signature) {
return nil, errors.New("revocation list verification failed")
}
return revoList, nil
}
func CreateRevocationList(privateRootKey RootKey, expiration time.Duration) ([]byte, []byte, error) {
now := time.Now()
rl := RevocationList{
Revoked: make(map[KeyID]time.Time),
LastUpdated: now.UTC(),
ExpiresAt: now.Add(expiration).UTC(),
}
signature, err := signRevocationList(privateRootKey, rl)
if err != nil {
return nil, nil, fmt.Errorf("failed to sign revocation list: %w", err)
}
rlData, err := json.Marshal(&rl)View on GitHub (pinned to 93e97f4bf1)
Solutions
- Update the NetBird client to the latest release so its embedded root keys can verify the current revocation list.
- Clear any cached revocation list artifact and re-download from the official source, then retry.
- If it persists, do not skip revocation checking; report it to the NetBird maintainers via the security policy.
Example fix
// before: ignoring revocation verification failure and proceeding
list, err := reposign.VerifyRevocationList(data, sig)
if err != nil {
log.Warnf("revocation check failed, assuming no revocations") // NEVER
}
// after: fail closed
list, err := reposign.VerifyRevocationList(data, sig)
if err != nil {
return fmt.Errorf("refusing update, revocation list unverifiable: %w", err)
} Defensive patterns
Strategy: try-catch
Try / catch
list, err := reposign.VerifyRevocationList(data, sig)
if err != nil {
// Fail closed: an unverifiable revocation list must not be treated as
// 'no revocations'. Abort the update and surface the error.
return fmt.Errorf("update aborted, revocation list untrusted: %w", err)
} Prevention
- Update the agent promptly after root-key rotations so revocation lists verify.
- Never skip revocation checks on failure or fall back to an empty list.
- Clear caches only from trusted sources; do not hand-edit cached list files.
When it happens
Trigger: The revocation list was signed by a rotated root key unknown to this client build; the list file is corrupted or truncated so the signed bytes differ; the list was tampered with (attacker stripping revocations). Note the timestamp-vs-LastUpdated skew check runs earlier, so reaching this line means the signature itself does not verify.
Common situations: Old client builds after root-key rotation; cached/stale revocation list from a mirror; intercepted or corrupted downloads. Because accepting an unverified list would let revoked keys through, the updater refuses to continue with it.
Related errors
- failed to verify signature of artifact keys
- no keys found in bundle
- failed to decode PEM data
- failed to read signature file: %w
- failed to validate revocation list: %w
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/841553d1a7c0d0de.
Report an issue: GitHub.