Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
124 commits
Select commit Hold shift + click to select a range
bc59749
Feature: Auto-update client
mohamed-essam Jul 30, 2025
c632878
Resolve comments
mohamed-essam Aug 19, 2025
762b9b7
Restructure version.Update to use channel
mohamed-essam Aug 20, 2025
84501a3
Fix deadlock issues
mohamed-essam Aug 25, 2025
58d4812
Define constants for version semantics
mohamed-essam Aug 26, 2025
d2e198b
Fix lint
mohamed-essam Aug 26, 2025
59ae92c
Refactor handleAutoUpdateVersion to outside handleSync
mohamed-essam Aug 26, 2025
6025eb1
Add unit tests
mohamed-essam Sep 2, 2025
ecf1e90
Merge branch 'main' into feat/auto-upgrade
mohamed-essam Sep 7, 2025
ec47a84
Remove testing.T.Context() as it's added in go1.24
mohamed-essam Sep 8, 2025
d19f829
Move autoUpdateVersion inside NetworkMap
mohamed-essam Sep 15, 2025
02afd4e
Move to networkMap.PeerConfig
mohamed-essam Sep 16, 2025
5042339
Update management/server/account.go
mlsmaycon Sep 20, 2025
ad3985a
Merge pull request #4504 from netbirdio/sub-feat/auto-upgrade/move-ve…
mohamed-essam Sep 21, 2025
b070304
Modify client-side behavior
mohamed-essam Oct 1, 2025
e04b989
Change ProgressBarInfinite to Updating... label
mohamed-essam Oct 1, 2025
723c418
Merge branch 'main' into feat/auto-upgrade
mohamed-essam Oct 6, 2025
0d2ce56
Merge branch 'feat/auto-upgrade' into auto-upgrade-mod
mohamed-essam Oct 6, 2025
b37ba44
Resolve issues
mohamed-essam Oct 8, 2025
436d740
Merge branch 'feat/auto-upgrade' into auto-upgrade-mod
mohamed-essam Oct 8, 2025
d5ea408
Resolve issues
mohamed-essam Oct 8, 2025
5556ff3
Merge pull request #4563 from netbirdio/auto-upgrade-mod
mohamed-essam Oct 12, 2025
582ff1f
Fix auto-update message handling
pappz Oct 13, 2025
9ae48a0
Remove unused codes and remove unnecessary variables
pappz Oct 13, 2025
7fa926d
Fix deadlock
pappz Oct 13, 2025
6200aaf
Fix state handling
pappz Oct 13, 2025
7d846bf
Fix nil pointer exception in expectedSemVer
pappz Oct 13, 2025
bab5cd4
Clean up temp dir
pappz Oct 13, 2025
cd19f4d
Code cleaning in updateState
pappz Oct 13, 2025
1354096
Fix windows build
pappz Oct 13, 2025
18f884f
- fix nil pointer for context
pappz Oct 13, 2025
9313b49
Fix Windows installer
pappz Oct 14, 2025
6eee52b
Fix auto update success message check
pappz Oct 15, 2025
030ddae
Merge branch 'main' into feat/auto-upgrade
pappz Oct 27, 2025
b65a234
Use updater process
pappz Oct 27, 2025
4e3ff1c
Code cleaning
pappz Oct 28, 2025
c0dd46e
Run the update check only when the user clicks on the "Connect" menu.
pappz Oct 28, 2025
99e2932
Clean up command line parameters
pappz Oct 28, 2025
f5dea2f
Add dry-run
pappz Oct 29, 2025
53127ca
Fix updater binary name
pappz Oct 29, 2025
ebd9406
Restart UI with proper session
pappz Oct 29, 2025
40546f4
Handle on UI the failed installation
pappz Oct 29, 2025
21b0b18
Add logs to debug bundle
pappz Oct 31, 2025
d04dfc5
Clean up installer file
pappz Oct 31, 2025
f211c51
Clean up installer file
pappz Oct 31, 2025
41b896e
Fix typo
pappz Oct 31, 2025
b27ff47
Add doc
pappz Oct 31, 2025
6d1ab80
Move Darwin installer steps into installer pkg
pappz Nov 3, 2025
52f61f9
Move installer to ui code
pappz Nov 3, 2025
7506946
Move download logic into installer
pappz Nov 3, 2025
0034d1f
Fix tests
pappz Nov 4, 2025
0f8ae14
Remove updater window from main UI
pappz Nov 4, 2025
4990b26
Handle results on UI
pappz Nov 4, 2025
1b15377
- Validate version
pappz Nov 4, 2025
fb912b5
Fix command line parser
pappz Nov 4, 2025
b029539
Exit from updater UI after close window
pappz Nov 4, 2025
8fa5ab1
Fix ui start on Darwin
pappz Nov 4, 2025
9fa532a
Use task scheduler on Windows
pappz Nov 5, 2025
424bd88
Revert the installer from UI process
pappz Nov 5, 2025
969e78b
Remove code duplications and fix mistakes on Darwin
pappz Nov 5, 2025
1444fac
Fix default temp dir usage
pappz Nov 5, 2025
64a11cc
Fix unit test
pappz Nov 5, 2025
7eb341b
Clean up on start time only
pappz Nov 5, 2025
bc29b1d
- add version for ui
pappz Nov 5, 2025
e001535
Fix Linux build
pappz Nov 6, 2025
fa50556
Fix error handling
pappz Nov 6, 2025
9e69bf7
Remove unused function
pappz Nov 6, 2025
f73da63
Disable new ui code on i386
pappz Nov 6, 2025
94e2db4
Add initial signature code
pappz Nov 6, 2025
28065e5
Run go mod tidy
pappz Nov 6, 2025
416530d
Fixe and add cmd
pappz Nov 7, 2025
5cd2125
Code cleaning
pappz Nov 7, 2025
e5110c9
Fix key marshalling
pappz Nov 7, 2025
d3fd3f0
Add tests
pappz Nov 7, 2025
2d60fbd
Separate constants
pappz Nov 9, 2025
ae20036
Add expiration for revocation list
pappz Nov 9, 2025
e3ed5df
Add command line revocation list check
pappz Nov 10, 2025
949c095
Add artifact verification check command
pappz Nov 10, 2025
12eea40
Remove hardcoded test values
pappz Nov 10, 2025
6a52694
Use cmd instead of fmt in signer commands
pappz Nov 10, 2025
f81ef14
Reduce complexity
pappz Nov 10, 2025
7a4cedc
Fix unit tests
pappz Nov 10, 2025
63d93f1
Fix lint issue
pappz Nov 10, 2025
9b8195d
Reduce cognitive complexity
pappz Nov 10, 2025
260c09b
Move update error results to connect layer from engine
pappz Nov 11, 2025
e10261e
Add dev root key
pappz Nov 11, 2025
31d9256
Fix artifact key creation
pappz Nov 11, 2025
02f3c01
Fix file path
pappz Nov 11, 2025
c003ba5
Add doc
pappz Nov 11, 2025
8812555
Update doc
pappz Nov 11, 2025
f15c80c
Fix URLs in test
pappz Nov 11, 2025
b1d509e
Do not hide window in case of error
pappz Nov 11, 2025
7656b38
Merge branch 'main' into feat/auto-upgrade
pappz Nov 12, 2025
a823f8c
Merge branch 'feat/auto-upgrade' into feat/auto-upgrade-fork
pappz Nov 12, 2025
eb2288e
Add environment key handling
pappz Nov 12, 2025
7eadad9
Disable auto-update on profile switch
pappz Nov 20, 2025
245b508
Fix description
pappz Nov 20, 2025
7f0db67
Update root-pub key for tests
pappz Nov 24, 2025
0ef8885
Update the root key and the final URLs
pappz Nov 24, 2025
4ded549
Remove debug log
pappz Nov 26, 2025
e19a9e1
Fix file watcher race condition
pappz Nov 28, 2025
88d96b8
Merge branch 'main' into feat/auto-upgrade-fork
pappz Nov 28, 2025
9d66e45
Fix command exec on Darwin
pappz Nov 28, 2025
ff78827
Try to handle volume
pappz Nov 28, 2025
3be2d63
Update downloadable flags
pappz Nov 28, 2025
a64dd3e
Regenerate proto file after merge
pappz Nov 29, 2025
fcb8c38
Do not run service on unsupported platform
pappz Nov 29, 2025
334f155
Update mgm proto after merge conflict
pappz Nov 29, 2025
1f9f5bd
Add missing import
pappz Nov 29, 2025
f0ef2e6
Fix engine.go
pappz Nov 29, 2025
0ef5765
Remove unused variable
pappz Nov 29, 2025
caa96fc
Fix ToSyncResponse in mgm
pappz Nov 29, 2025
aec7c0f
Update doc
pappz Nov 29, 2025
ae47626
Fix potential nil pointer in dir check
pappz Nov 29, 2025
ea8957e
Fix ui compilation
pappz Nov 29, 2025
57ff5fa
Do not start manager on every network map update
pappz Nov 29, 2025
18a24be
Fix test
pappz Nov 29, 2025
6d60e0c
Remove todo comment
pappz Dec 1, 2025
b77652b
Update repo path
pappz Dec 2, 2025
6862438
Set test repo for release page
pappz Dec 3, 2025
03418cc
Merge branch 'main' into feat/auto-upgrade-fork
pappz Dec 5, 2025
9fa55e7
Detach the main process from updater and kill UI
pappz Dec 6, 2025
941385b
Support Intel and Apple in brew installer
pappz Dec 6, 2025
095e742
Register UpdateState in state manager
pappz Dec 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions client/android/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ func (c *Client) Run(urlOpener URLOpener, isAndroidTV bool, dns *DNSList, dnsRea

// todo do not throw error in case of cancelled context
ctx = internal.CtxInitState(ctx)
c.connectClient = internal.NewConnectClient(ctx, cfg, c.recorder)
c.connectClient = internal.NewConnectClient(ctx, cfg, c.recorder, false)
return c.connectClient.RunOnAndroid(c.tunAdapter, c.iFaceDiscover, c.networkChangeListener, slices.Clone(dns.items), dnsReadyListener, c.stateFile)
}

Expand All @@ -149,7 +149,7 @@ func (c *Client) RunWithoutLogin(dns *DNSList, dnsReadyListener DnsReadyListener

// todo do not throw error in case of cancelled context
ctx = internal.CtxInitState(ctx)
c.connectClient = internal.NewConnectClient(ctx, cfg, c.recorder)
c.connectClient = internal.NewConnectClient(ctx, cfg, c.recorder, false)
return c.connectClient.RunOnAndroid(c.tunAdapter, c.iFaceDiscover, c.networkChangeListener, slices.Clone(dns.items), dnsReadyListener, c.stateFile)
}

Expand Down
3 changes: 3 additions & 0 deletions client/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ var (

// Execute executes the root command.
func Execute() error {
if isUpdateBinary() {
return updateCmd.Execute()
}
return rootCmd.Execute()
}

Expand Down
176 changes: 176 additions & 0 deletions client/cmd/signer/artifactkey.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
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
}
Loading
Loading