netbirdio/netbird · critical
failed to verify signature of artifact keys
Error message
failed to verify signature of artifact keys
What it means
Returned by the reposign artifact-key verification path (client/internal/updater/reposign/artifact.go:185) when verifyAny fails to match the bundle signature against any of the trusted public root keys embedded in the client. This is the update supply-chain trust check: the artifact key bundle (data || little-endian timestamp) must verify against at least one root key, otherwise the artifact keys are rejected. The check fails closed by design.
Source
Thrown at client/internal/updater/reposign/artifact.go:185
now := time.Now().UTC()
if signature.Timestamp.After(now.Add(maxClockSkew)) {
err := fmt.Errorf("signature timestamp is in the future: %v", signature.Timestamp)
log.Debugf("artifact signature error: %v", err)
return nil, err
}
if now.Sub(signature.Timestamp) > maxArtifactKeySignatureAge {
err := fmt.Errorf("signature is too old: %v (created %v)", now.Sub(signature.Timestamp), signature.Timestamp)
log.Debugf("artifact signature error: %v", err)
return nil, err
}
// Reconstruct the signed message: artifact_key_data || timestamp
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("failed to verify signature of artifact keys")
}
pubKeys, err := parsePublicKeyBundle(data, tagArtifactPublic)
if err != nil {
log.Debugf("failed to parse public keys: %s", err)
return nil, err
}
validKeys := make([]PublicKey, 0, len(pubKeys))
for _, pubKey := range pubKeys {
// Filter out expired keys
if !pubKey.Metadata.ExpiresAt.IsZero() && now.After(pubKey.Metadata.ExpiresAt) {
log.Debugf("Key %s is expired at %v (current time %v)",
pubKey.Metadata.ID, pubKey.Metadata.ExpiresAt, now)
continue
}
if revocationList != nil {View on GitHub (pinned to 93e97f4bf1)
Solutions
- Update the NetBird client to the latest release, which embeds the current set of trusted root keys.
- Re-download the update artifact from the official source and retry, to rule out corruption.
- If it persists on the latest client, treat it as a potential compromise: do not bypass the check, report it to the NetBird maintainers via the security policy.
Example fix
// before: catching the error and continuing with the update anyway
if _, err := reposign.VerifyArtifactKeys(data, sig); err != nil {
log.Warnf("signature check failed, continuing: %v", err) // NEVER do this
}
// after: fail closed, abort the update
if _, err := reposign.VerifyArtifactKeys(data, sig); err != nil {
return fmt.Errorf("refusing update, artifact key signature invalid: %w", err)
} Defensive patterns
Strategy: try-catch
Try / catch
keys, err := reposign.VerifyArtifactKeys(data, sig, now)
if err != nil {
// Fail closed: abort the update. Never downgrade this to a warning.
return fmt.Errorf("update aborted, artifact keys untrusted: %w", err)
} Prevention
- Keep the client up to date so its embedded root keys match current signings.
- Never wrap signature failures in retry-with-ignore logic or cache bypasses.
- Download artifacts only from official sources over TLS.
When it happens
Trigger: Artifact keys were signed by a root key that postdates the root keys baked into this client build (key rotation); the downloaded keys file is corrupted or truncated so the signed bytes differ; the bundle was tampered with (MITM or compromised mirror); clock skew combined with the maxArtifactKeySignatureAge check is handled separately, so reaching this line means the signature itself is invalid.
Common situations: Running an old NetBird release after the project rotated its signing root keys; a proxied or cached download corrupting the artifact; an attacker-controlled update source. Any occurrence means the update must not proceed.
Related errors
- revocation list verification failed
- no keys found in bundle
- failed to decode PEM data
- address %s is part of the NetBird network %s, refusing to wr
- management client is not initialised
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/91805716d0ebd700.
Report an issue: GitHub.