mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-19 16:56:39 +00:00
175 lines
6.9 KiB
Go
175 lines
6.9 KiB
Go
// Package reposign implements a cryptographic signing and verification system
|
|
// for NetBird software update artifacts. It provides a hierarchical key
|
|
// management system with support for key rotation, revocation, and secure
|
|
// artifact distribution.
|
|
//
|
|
// # Architecture
|
|
//
|
|
// The package uses a two-tier key hierarchy:
|
|
//
|
|
// - Root Keys: Long-lived keys that sign artifact keys. These are embedded
|
|
// in the client binary and establish the root of trust. Root keys should
|
|
// be kept offline and highly secured.
|
|
//
|
|
// - Artifact Keys: Short-lived keys that sign release artifacts (binaries,
|
|
// packages, etc.). These are rotated regularly and can be revoked if
|
|
// compromised. Artifact keys are signed by root keys and distributed via
|
|
// a public repository.
|
|
//
|
|
// This separation allows for operational flexibility: artifact keys can be
|
|
// rotated frequently without requiring client updates, while root keys remain
|
|
// stable and embedded in the software.
|
|
//
|
|
// # Cryptographic Primitives
|
|
//
|
|
// The package uses strong, modern cryptographic algorithms:
|
|
// - Ed25519: Fast, secure digital signatures (no timing attacks)
|
|
// - BLAKE2s-256: Fast cryptographic hash for artifacts
|
|
// - SHA-256: Key ID generation
|
|
// - JSON: Structured key and signature serialization
|
|
// - PEM: Standard key encoding format
|
|
//
|
|
// # Security Features
|
|
//
|
|
// Timestamp Binding:
|
|
// - All signatures include cryptographically-bound timestamps
|
|
// - Prevents replay attacks and enforces signature freshness
|
|
// - Clock skew tolerance: 5 minutes
|
|
//
|
|
// Key Expiration:
|
|
// - All keys have expiration times
|
|
// - Expired keys are automatically rejected
|
|
// - Signing with an expired key fails immediately
|
|
//
|
|
// Key Revocation:
|
|
// - Compromised keys can be revoked via a signed revocation list
|
|
// - Revocation list is checked during artifact validation
|
|
// - Revoked keys are filtered out before artifact verification
|
|
//
|
|
// # File Structure
|
|
//
|
|
// The package expects the following file layout in the key repository:
|
|
//
|
|
// signrepo/
|
|
// artifact-key-pub.pem # Bundle of artifact public keys
|
|
// artifact-key-pub.pem.sig # Root signature of the bundle
|
|
// revocation-list.json # List of revoked key IDs
|
|
// revocation-list.json.sig # Root signature of revocation list
|
|
//
|
|
// And in the artifacts repository:
|
|
//
|
|
// releases/
|
|
// v0.28.0/
|
|
// netbird-linux-amd64
|
|
// netbird-linux-amd64.sig # Artifact signature
|
|
// netbird-darwin-amd64
|
|
// netbird-darwin-amd64.sig
|
|
// ...
|
|
//
|
|
// # Embedded Root Keys
|
|
//
|
|
// Root public keys are embedded in the client binary at compile time:
|
|
// - Production keys: certs/ directory
|
|
// - Development keys: certsdev/ directory
|
|
//
|
|
// The build tag determines which keys are embedded:
|
|
// - Production builds: //go:build !devartifactsign
|
|
// - Development builds: //go:build devartifactsign
|
|
//
|
|
// This ensures that development artifacts cannot be verified using production
|
|
// keys and vice versa.
|
|
//
|
|
// # Key Rotation Strategies
|
|
//
|
|
// Root Key Rotation:
|
|
//
|
|
// Root keys can be rotated without breaking existing clients by leveraging
|
|
// the multi-key verification system. The loadEmbeddedPublicKeys function
|
|
// reads ALL files from the certs/ directory and accepts signatures from ANY
|
|
// of the embedded root keys.
|
|
//
|
|
// To rotate root keys:
|
|
//
|
|
// 1. Generate a new root key pair:
|
|
// newRootKey, privPEM, pubPEM, err := GenerateRootKey(10 * 365 * 24 * time.Hour)
|
|
//
|
|
// 2. Add the new public key to the certs/ directory as a new file:
|
|
// certs/
|
|
// root-pub-2024.pem # Old key (keep this!)
|
|
// root-pub-2025.pem # New key (add this)
|
|
//
|
|
// 3. Build new client versions with both keys embedded. The verification
|
|
// will accept signatures from either key.
|
|
//
|
|
// 4. Start signing new artifact keys with the new root key. Old clients
|
|
// with only the old root key will reject these, but new clients with
|
|
// both keys will accept them.
|
|
//
|
|
// Each file in certs/ can contain a single key or a bundle of keys (multiple
|
|
// PEM blocks). The system will parse all keys from all files and use them
|
|
// for verification. This provides maximum flexibility for key management.
|
|
//
|
|
// Important: Never remove all old root keys at once. Always maintain at least
|
|
// one overlapping key between releases to ensure smooth transitions.
|
|
//
|
|
// Artifact Key Rotation:
|
|
//
|
|
// Artifact keys should be rotated regularly (e.g., every 90 days) using the
|
|
// bundling mechanism. The BundleArtifactKeys function allows multiple artifact
|
|
// keys to be bundled together in a single signed package, and ValidateArtifact
|
|
// will accept signatures from ANY key in the bundle.
|
|
//
|
|
// To rotate artifact keys smoothly:
|
|
//
|
|
// 1. Generate a new artifact key while keeping the old one:
|
|
// newKey, newPrivPEM, newPubPEM, newSig, err := GenerateArtifactKey(rootKey, 90 * 24 * time.Hour)
|
|
// // Keep oldPubPEM and oldKey available
|
|
//
|
|
// 2. Create a bundle containing both old and new public keys
|
|
//
|
|
// 3. Upload the bundle and its signature to the key repository:
|
|
// signrepo/artifact-key-pub.pem # Contains both keys
|
|
// signrepo/artifact-key-pub.pem.sig # Root signature
|
|
//
|
|
// 4. Start signing new releases with the NEW key, but keep the bundle
|
|
// unchanged. Clients will download the bundle (containing both keys)
|
|
// and accept signatures from either key.
|
|
//
|
|
// Key bundle validation workflow:
|
|
// 1. Client downloads artifact-key-pub.pem and artifact-key-pub.pem.sig
|
|
// 2. ValidateArtifactKeys verifies the bundle signature with ANY embedded root key
|
|
// 3. ValidateArtifactKeys parses all public keys from the bundle
|
|
// 4. ValidateArtifactKeys filters out expired or revoked keys
|
|
// 5. When verifying an artifact, ValidateArtifact tries each key until one succeeds
|
|
//
|
|
// This multi-key acceptance model enables overlapping validity periods and
|
|
// smooth transitions without client update requirements.
|
|
//
|
|
// # Best Practices
|
|
//
|
|
// Root Key Management:
|
|
// - Generate root keys offline on an air-gapped machine
|
|
// - Store root private keys in hardware security modules (HSM) if possible
|
|
// - Use separate root keys for production and development
|
|
// - Rotate root keys infrequently (e.g., every 5-10 years)
|
|
// - Plan for root key rotation: embed multiple root public keys
|
|
//
|
|
// Artifact Key Management:
|
|
// - Rotate artifact keys regularly (e.g., every 90 days)
|
|
// - Use separate artifact keys for different release channels if needed
|
|
// - Revoke keys immediately upon suspected compromise
|
|
// - Bundle multiple artifact keys to enable smooth rotation
|
|
//
|
|
// Signing Process:
|
|
// - Sign artifacts in a secure CI/CD environment
|
|
// - Never commit private keys to version control
|
|
// - Use environment variables or secret management for keys
|
|
// - Verify signatures immediately after signing
|
|
//
|
|
// Distribution:
|
|
// - Serve keys and revocation lists from a reliable CDN
|
|
// - Use HTTPS for all key and artifact downloads
|
|
// - Monitor download failures and signature verification failures
|
|
// - Keep revocation list up to date
|
|
package reposign
|