mirror of
https://github.com/netbirdio/netbird.git
synced 2026-03-31 06:24:18 -04: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
177 lines
6.8 KiB
Go
177 lines
6.8 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"time"
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
"github.com/netbirdio/netbird/client/internal/updater/reposign"
|
|
)
|
|
|
|
var (
|
|
bundlePubKeysRootPrivKeyFile string
|
|
bundlePubKeysPubKeyFiles []string
|
|
bundlePubKeysFile string
|
|
|
|
createArtifactKeyRootPrivKeyFile string
|
|
createArtifactKeyPrivKeyFile string
|
|
createArtifactKeyPubKeyFile string
|
|
createArtifactKeyExpiration time.Duration
|
|
)
|
|
|
|
var createArtifactKeyCmd = &cobra.Command{
|
|
Use: "create-artifact-key",
|
|
Short: "Create a new artifact signing key",
|
|
Long: `Generate a new artifact signing key pair signed by the root private key.
|
|
The artifact key will be used to sign software artifacts/updates.`,
|
|
SilenceUsage: true,
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
if createArtifactKeyExpiration <= 0 {
|
|
return fmt.Errorf("--expiration must be a positive duration (e.g., 720h, 365d, 8760h)")
|
|
}
|
|
|
|
if err := handleCreateArtifactKey(cmd, createArtifactKeyRootPrivKeyFile, createArtifactKeyPrivKeyFile, createArtifactKeyPubKeyFile, createArtifactKeyExpiration); err != nil {
|
|
return fmt.Errorf("failed to create artifact key: %w", err)
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
var bundlePubKeysCmd = &cobra.Command{
|
|
Use: "bundle-pub-keys",
|
|
Short: "Bundle multiple artifact public keys into a signed package",
|
|
Long: `Bundle one or more artifact public keys into a signed package using the root private key.
|
|
This command is typically used to distribute or authorize a set of valid artifact signing keys.`,
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
if len(bundlePubKeysPubKeyFiles) == 0 {
|
|
return fmt.Errorf("at least one --artifact-pub-key-file must be provided")
|
|
}
|
|
|
|
if err := handleBundlePubKeys(cmd, bundlePubKeysRootPrivKeyFile, bundlePubKeysPubKeyFiles, bundlePubKeysFile); err != nil {
|
|
return fmt.Errorf("failed to bundle public keys: %w", err)
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
func init() {
|
|
rootCmd.AddCommand(createArtifactKeyCmd)
|
|
|
|
createArtifactKeyCmd.Flags().StringVar(&createArtifactKeyRootPrivKeyFile, "root-private-key-file", "", "Path to the root private key file used to sign the artifact key")
|
|
createArtifactKeyCmd.Flags().StringVar(&createArtifactKeyPrivKeyFile, "artifact-priv-key-file", "", "Path where the artifact private key will be saved")
|
|
createArtifactKeyCmd.Flags().StringVar(&createArtifactKeyPubKeyFile, "artifact-pub-key-file", "", "Path where the artifact public key will be saved")
|
|
createArtifactKeyCmd.Flags().DurationVar(&createArtifactKeyExpiration, "expiration", 0, "Expiration duration for the artifact key (e.g., 720h, 365d, 8760h)")
|
|
|
|
if err := createArtifactKeyCmd.MarkFlagRequired("root-private-key-file"); err != nil {
|
|
panic(fmt.Errorf("mark root-private-key-file as required: %w", err))
|
|
}
|
|
if err := createArtifactKeyCmd.MarkFlagRequired("artifact-priv-key-file"); err != nil {
|
|
panic(fmt.Errorf("mark artifact-priv-key-file as required: %w", err))
|
|
}
|
|
if err := createArtifactKeyCmd.MarkFlagRequired("artifact-pub-key-file"); err != nil {
|
|
panic(fmt.Errorf("mark artifact-pub-key-file as required: %w", err))
|
|
}
|
|
if err := createArtifactKeyCmd.MarkFlagRequired("expiration"); err != nil {
|
|
panic(fmt.Errorf("mark expiration as required: %w", err))
|
|
}
|
|
|
|
rootCmd.AddCommand(bundlePubKeysCmd)
|
|
|
|
bundlePubKeysCmd.Flags().StringVar(&bundlePubKeysRootPrivKeyFile, "root-private-key-file", "", "Path to the root private key file used to sign the bundle")
|
|
bundlePubKeysCmd.Flags().StringArrayVar(&bundlePubKeysPubKeyFiles, "artifact-pub-key-file", nil, "Path(s) to the artifact public key files to include in the bundle (can be repeated)")
|
|
bundlePubKeysCmd.Flags().StringVar(&bundlePubKeysFile, "bundle-pub-key-file", "", "Path where the public keys will be saved")
|
|
|
|
if err := bundlePubKeysCmd.MarkFlagRequired("root-private-key-file"); err != nil {
|
|
panic(fmt.Errorf("mark root-private-key-file as required: %w", err))
|
|
}
|
|
if err := bundlePubKeysCmd.MarkFlagRequired("artifact-pub-key-file"); err != nil {
|
|
panic(fmt.Errorf("mark artifact-pub-key-file as required: %w", err))
|
|
}
|
|
if err := bundlePubKeysCmd.MarkFlagRequired("bundle-pub-key-file"); err != nil {
|
|
panic(fmt.Errorf("mark bundle-pub-key-file as required: %w", err))
|
|
}
|
|
}
|
|
|
|
func handleCreateArtifactKey(cmd *cobra.Command, rootPrivKeyFile, artifactPrivKeyFile, artifactPubKeyFile string, expiration time.Duration) error {
|
|
cmd.Println("Creating new artifact signing key...")
|
|
|
|
privKeyPEM, err := os.ReadFile(rootPrivKeyFile)
|
|
if err != nil {
|
|
return fmt.Errorf("read root private key file: %w", err)
|
|
}
|
|
|
|
privateRootKey, err := reposign.ParseRootKey(privKeyPEM)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to parse private root key: %w", err)
|
|
}
|
|
|
|
artifactKey, privPEM, pubPEM, signature, err := reposign.GenerateArtifactKey(privateRootKey, expiration)
|
|
if err != nil {
|
|
return fmt.Errorf("generate artifact key: %w", err)
|
|
}
|
|
|
|
if err := os.WriteFile(artifactPrivKeyFile, privPEM, 0o600); err != nil {
|
|
return fmt.Errorf("write private key file (%s): %w", artifactPrivKeyFile, err)
|
|
}
|
|
|
|
if err := os.WriteFile(artifactPubKeyFile, pubPEM, 0o600); err != nil {
|
|
return fmt.Errorf("write public key file (%s): %w", artifactPubKeyFile, err)
|
|
}
|
|
|
|
signatureFile := artifactPubKeyFile + ".sig"
|
|
if err := os.WriteFile(signatureFile, signature, 0o600); err != nil {
|
|
return fmt.Errorf("write signature file (%s): %w", signatureFile, err)
|
|
}
|
|
|
|
cmd.Printf("✅ Artifact key created successfully.\n")
|
|
cmd.Printf("%s\n", artifactKey.String())
|
|
return nil
|
|
}
|
|
|
|
func handleBundlePubKeys(cmd *cobra.Command, rootPrivKeyFile string, artifactPubKeyFiles []string, bundlePubKeysFile string) error {
|
|
cmd.Println("📦 Bundling public keys into signed package...")
|
|
|
|
privKeyPEM, err := os.ReadFile(rootPrivKeyFile)
|
|
if err != nil {
|
|
return fmt.Errorf("read root private key file: %w", err)
|
|
}
|
|
|
|
privateRootKey, err := reposign.ParseRootKey(privKeyPEM)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to parse private root key: %w", err)
|
|
}
|
|
|
|
publicKeys := make([]reposign.PublicKey, 0, len(artifactPubKeyFiles))
|
|
for _, pubFile := range artifactPubKeyFiles {
|
|
pubPem, err := os.ReadFile(pubFile)
|
|
if err != nil {
|
|
return fmt.Errorf("read public key file: %w", err)
|
|
}
|
|
|
|
pk, err := reposign.ParseArtifactPubKey(pubPem)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to parse artifact key: %w", err)
|
|
}
|
|
publicKeys = append(publicKeys, pk)
|
|
}
|
|
|
|
parsedKeys, signature, err := reposign.BundleArtifactKeys(privateRootKey, publicKeys)
|
|
if err != nil {
|
|
return fmt.Errorf("bundle artifact keys: %w", err)
|
|
}
|
|
|
|
if err := os.WriteFile(bundlePubKeysFile, parsedKeys, 0o600); err != nil {
|
|
return fmt.Errorf("write public keys file (%s): %w", bundlePubKeysFile, err)
|
|
}
|
|
|
|
signatureFile := bundlePubKeysFile + ".sig"
|
|
if err := os.WriteFile(signatureFile, signature, 0o600); err != nil {
|
|
return fmt.Errorf("write signature file (%s): %w", signatureFile, err)
|
|
}
|
|
|
|
cmd.Printf("✅ Bundle created with %d public keys.\n", len(artifactPubKeyFiles))
|
|
return nil
|
|
}
|