mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-05 00:44:10 -04:00
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/updatemanager/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
|
|
}
|