mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-16 15:26:40 +00:00
Auto-update logic moved out of the UI into a dedicated updatemanager.Manager service that runs in the connection layer. The UI no longer polls or checks for updates independently. The update manager supports three modes driven by the management server's auto-update policy: No policy set by mgm: checks GitHub for the latest version and notifies the user (previous behavior, now centralized) mgm enforces update: the "About" menu triggers installation directly instead of just downloading the file — user still initiates the action mgm forces update: installation proceeds automatically without user interaction updateManager lifecycle is now owned by daemon, giving the daemon server direct control via a new TriggerUpdate RPC Introduces EngineServices struct to group external service dependencies passed to NewEngine, reducing its argument count from 11 to 4
221 lines
7.6 KiB
Go
221 lines
7.6 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"time"
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
"github.com/netbirdio/netbird/client/internal/updater/reposign"
|
|
)
|
|
|
|
const (
|
|
defaultRevocationListExpiration = 365 * 24 * time.Hour // 1 year
|
|
)
|
|
|
|
var (
|
|
keyID string
|
|
revocationListFile string
|
|
privateRootKeyFile string
|
|
publicRootKeyFile string
|
|
signatureFile string
|
|
expirationDuration time.Duration
|
|
)
|
|
|
|
var createRevocationListCmd = &cobra.Command{
|
|
Use: "create-revocation-list",
|
|
Short: "Create a new revocation list signed by the private root key",
|
|
SilenceUsage: true,
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
return handleCreateRevocationList(cmd, revocationListFile, privateRootKeyFile)
|
|
},
|
|
}
|
|
|
|
var extendRevocationListCmd = &cobra.Command{
|
|
Use: "extend-revocation-list",
|
|
Short: "Extend an existing revocation list with a given key ID",
|
|
SilenceUsage: true,
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
return handleExtendRevocationList(cmd, keyID, revocationListFile, privateRootKeyFile)
|
|
},
|
|
}
|
|
|
|
var verifyRevocationListCmd = &cobra.Command{
|
|
Use: "verify-revocation-list",
|
|
Short: "Verify a revocation list signature using the public root key",
|
|
SilenceUsage: true,
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
return handleVerifyRevocationList(cmd, revocationListFile, signatureFile, publicRootKeyFile)
|
|
},
|
|
}
|
|
|
|
func init() {
|
|
rootCmd.AddCommand(createRevocationListCmd)
|
|
rootCmd.AddCommand(extendRevocationListCmd)
|
|
rootCmd.AddCommand(verifyRevocationListCmd)
|
|
|
|
createRevocationListCmd.Flags().StringVar(&revocationListFile, "revocation-list-file", "", "Path to the existing revocation list file")
|
|
createRevocationListCmd.Flags().StringVar(&privateRootKeyFile, "private-root-key", "", "Path to the private root key PEM file")
|
|
createRevocationListCmd.Flags().DurationVar(&expirationDuration, "expiration", defaultRevocationListExpiration, "Expiration duration for the revocation list (e.g., 8760h for 1 year)")
|
|
if err := createRevocationListCmd.MarkFlagRequired("revocation-list-file"); err != nil {
|
|
panic(err)
|
|
}
|
|
if err := createRevocationListCmd.MarkFlagRequired("private-root-key"); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
extendRevocationListCmd.Flags().StringVar(&keyID, "key-id", "", "ID of the key to extend the revocation list for")
|
|
extendRevocationListCmd.Flags().StringVar(&revocationListFile, "revocation-list-file", "", "Path to the existing revocation list file")
|
|
extendRevocationListCmd.Flags().StringVar(&privateRootKeyFile, "private-root-key", "", "Path to the private root key PEM file")
|
|
extendRevocationListCmd.Flags().DurationVar(&expirationDuration, "expiration", defaultRevocationListExpiration, "Expiration duration for the revocation list (e.g., 8760h for 1 year)")
|
|
if err := extendRevocationListCmd.MarkFlagRequired("key-id"); err != nil {
|
|
panic(err)
|
|
}
|
|
if err := extendRevocationListCmd.MarkFlagRequired("revocation-list-file"); err != nil {
|
|
panic(err)
|
|
}
|
|
if err := extendRevocationListCmd.MarkFlagRequired("private-root-key"); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
verifyRevocationListCmd.Flags().StringVar(&revocationListFile, "revocation-list-file", "", "Path to the revocation list file")
|
|
verifyRevocationListCmd.Flags().StringVar(&signatureFile, "signature-file", "", "Path to the signature file")
|
|
verifyRevocationListCmd.Flags().StringVar(&publicRootKeyFile, "public-root-key", "", "Path to the public root key PEM file")
|
|
if err := verifyRevocationListCmd.MarkFlagRequired("revocation-list-file"); err != nil {
|
|
panic(err)
|
|
}
|
|
if err := verifyRevocationListCmd.MarkFlagRequired("signature-file"); err != nil {
|
|
panic(err)
|
|
}
|
|
if err := verifyRevocationListCmd.MarkFlagRequired("public-root-key"); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
func handleCreateRevocationList(cmd *cobra.Command, revocationListFile string, privateRootKeyFile string) error {
|
|
privKeyPEM, err := os.ReadFile(privateRootKeyFile)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to read private root key file: %w", err)
|
|
}
|
|
|
|
privateRootKey, err := reposign.ParseRootKey(privKeyPEM)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to parse private root key: %w", err)
|
|
}
|
|
|
|
rlBytes, sigBytes, err := reposign.CreateRevocationList(*privateRootKey, expirationDuration)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create revocation list: %w", err)
|
|
}
|
|
|
|
if err := writeOutputFiles(revocationListFile, revocationListFile+".sig", rlBytes, sigBytes); err != nil {
|
|
return fmt.Errorf("failed to write output files: %w", err)
|
|
}
|
|
|
|
cmd.Println("✅ Revocation list created successfully")
|
|
return nil
|
|
}
|
|
|
|
func handleExtendRevocationList(cmd *cobra.Command, keyID, revocationListFile, privateRootKeyFile string) error {
|
|
privKeyPEM, err := os.ReadFile(privateRootKeyFile)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to read private root key file: %w", err)
|
|
}
|
|
|
|
privateRootKey, err := reposign.ParseRootKey(privKeyPEM)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to parse private root key: %w", err)
|
|
}
|
|
|
|
rlBytes, err := os.ReadFile(revocationListFile)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to read revocation list file: %w", err)
|
|
}
|
|
|
|
rl, err := reposign.ParseRevocationList(rlBytes)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to parse revocation list: %w", err)
|
|
}
|
|
|
|
kid, err := reposign.ParseKeyID(keyID)
|
|
if err != nil {
|
|
return fmt.Errorf("invalid key ID: %w", err)
|
|
}
|
|
|
|
newRLBytes, sigBytes, err := reposign.ExtendRevocationList(*privateRootKey, *rl, kid, expirationDuration)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to extend revocation list: %w", err)
|
|
}
|
|
|
|
if err := writeOutputFiles(revocationListFile, revocationListFile+".sig", newRLBytes, sigBytes); err != nil {
|
|
return fmt.Errorf("failed to write output files: %w", err)
|
|
}
|
|
|
|
cmd.Println("✅ Revocation list extended successfully")
|
|
return nil
|
|
}
|
|
|
|
func handleVerifyRevocationList(cmd *cobra.Command, revocationListFile, signatureFile, publicRootKeyFile string) error {
|
|
// Read revocation list file
|
|
rlBytes, err := os.ReadFile(revocationListFile)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to read revocation list file: %w", err)
|
|
}
|
|
|
|
// Read signature file
|
|
sigBytes, err := os.ReadFile(signatureFile)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to read signature file: %w", err)
|
|
}
|
|
|
|
// Read public root key file
|
|
pubKeyPEM, err := os.ReadFile(publicRootKeyFile)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to read public root key file: %w", err)
|
|
}
|
|
|
|
// Parse public root key
|
|
publicKey, err := reposign.ParseRootPublicKey(pubKeyPEM)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to parse public root key: %w", err)
|
|
}
|
|
|
|
// Parse signature
|
|
signature, err := reposign.ParseSignature(sigBytes)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to parse signature: %w", err)
|
|
}
|
|
|
|
// Validate revocation list
|
|
rl, err := reposign.ValidateRevocationList([]reposign.PublicKey{publicKey}, rlBytes, *signature)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to validate revocation list: %w", err)
|
|
}
|
|
|
|
// Display results
|
|
cmd.Println("✅ Revocation list signature is valid")
|
|
cmd.Printf("Last Updated: %s\n", rl.LastUpdated.Format(time.RFC3339))
|
|
cmd.Printf("Expires At: %s\n", rl.ExpiresAt.Format(time.RFC3339))
|
|
cmd.Printf("Number of revoked keys: %d\n", len(rl.Revoked))
|
|
|
|
if len(rl.Revoked) > 0 {
|
|
cmd.Println("\nRevoked Keys:")
|
|
for keyID, revokedTime := range rl.Revoked {
|
|
cmd.Printf(" - %s (revoked at: %s)\n", keyID, revokedTime.Format(time.RFC3339))
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func writeOutputFiles(rlPath, sigPath string, rlBytes, sigBytes []byte) error {
|
|
if err := os.WriteFile(rlPath, rlBytes, 0o600); err != nil {
|
|
return fmt.Errorf("failed to write revocation list file: %w", err)
|
|
}
|
|
if err := os.WriteFile(sigPath, sigBytes, 0o600); err != nil {
|
|
return fmt.Errorf("failed to write signature file: %w", err)
|
|
}
|
|
return nil
|
|
}
|