From 07d721285f812c4ed70ff66762dd1f03a0fb49e2 Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Wed, 6 Aug 2025 01:11:47 +0200 Subject: [PATCH] [misc]: Factorize SetFlagsFromEnvVars and add support for CREDENTIALS_DIRECTORY The code for setting up command flags values from environment variables was copied in three different places, this puts the helper function in the util package so that every component can use it. Furthermore, it now supports reading values from files in `$CREDENTIALS_DIRECTORY`, which allows direct integration with systemd's `LoadCredential` mechanism (but any platform can define such a variable and pass values as files). --- client/cmd/down.go | 2 +- client/cmd/login.go | 2 +- client/cmd/logout.go | 3 +- client/cmd/profile.go | 4 +-- client/cmd/root.go | 36 ++------------------ client/cmd/root_test.go | 3 +- client/cmd/service_controller.go | 4 +-- client/cmd/service_installer.go | 4 +-- client/cmd/ssh.go | 4 +-- client/cmd/status.go | 2 +- client/cmd/up.go | 4 +-- relay/cmd/env.go | 35 -------------------- relay/cmd/root.go | 2 +- signal/cmd/env.go | 35 -------------------- signal/cmd/run.go | 2 +- util/flags.go | 56 ++++++++++++++++++++++++++++++++ 16 files changed, 77 insertions(+), 121 deletions(-) delete mode 100644 relay/cmd/env.go delete mode 100644 signal/cmd/env.go create mode 100644 util/flags.go diff --git a/client/cmd/down.go b/client/cmd/down.go index cfa69bce226..778274d63a2 100644 --- a/client/cmd/down.go +++ b/client/cmd/down.go @@ -16,7 +16,7 @@ var downCmd = &cobra.Command{ Use: "down", Short: "down netbird connections", RunE: func(cmd *cobra.Command, args []string) error { - SetFlagsFromEnvVars(rootCmd) + util.SetFlagsFromEnvVars(rootCmd) cmd.SetOut(cmd.OutOrStdout()) diff --git a/client/cmd/login.go b/client/cmd/login.go index d6381f6e2a0..1a1a1c194ad 100644 --- a/client/cmd/login.go +++ b/client/cmd/login.go @@ -371,7 +371,7 @@ func isUnixRunningDesktop() bool { } func setEnvAndFlags(cmd *cobra.Command) error { - SetFlagsFromEnvVars(rootCmd) + util.SetFlagsFromEnvVars(rootCmd) cmd.SetOut(cmd.OutOrStdout()) diff --git a/client/cmd/logout.go b/client/cmd/logout.go index 071be5ca944..61be2bc789f 100644 --- a/client/cmd/logout.go +++ b/client/cmd/logout.go @@ -9,13 +9,14 @@ import ( "github.com/spf13/cobra" "github.com/netbirdio/netbird/client/proto" + "github.com/netbirdio/netbird/util" ) var logoutCmd = &cobra.Command{ Use: "logout", Short: "logout from the Netbird Management Service and delete peer", RunE: func(cmd *cobra.Command, args []string) error { - SetFlagsFromEnvVars(rootCmd) + util.SetFlagsFromEnvVars(rootCmd) cmd.SetOut(cmd.OutOrStdout()) diff --git a/client/cmd/profile.go b/client/cmd/profile.go index d420dcbd9f6..8ca0303c97e 100644 --- a/client/cmd/profile.go +++ b/client/cmd/profile.go @@ -53,8 +53,8 @@ var profileSelectCmd = &cobra.Command{ } func setupCmd(cmd *cobra.Command) error { - SetFlagsFromEnvVars(rootCmd) - SetFlagsFromEnvVars(cmd) + util.SetFlagsFromEnvVars(rootCmd) + util.SetFlagsFromEnvVars(cmd) cmd.SetOut(cmd.OutOrStdout()) diff --git a/client/cmd/root.go b/client/cmd/root.go index 86c76e6ab9e..2e271717301 100644 --- a/client/cmd/root.go +++ b/client/cmd/root.go @@ -18,11 +18,11 @@ import ( "github.com/cenkalti/backoff/v4" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" - "github.com/spf13/pflag" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" "github.com/netbirdio/netbird/client/internal/profilemanager" + "github.com/netbirdio/netbird/util" ) const ( @@ -194,38 +194,6 @@ func SetupCloseHandler(ctx context.Context, cancel context.CancelFunc) { }() } -// SetFlagsFromEnvVars reads and updates flag values from environment variables with prefix WT_ -func SetFlagsFromEnvVars(cmd *cobra.Command) { - flags := cmd.PersistentFlags() - flags.VisitAll(func(f *pflag.Flag) { - oldEnvVar := FlagNameToEnvVar(f.Name, "WT_") - - if value, present := os.LookupEnv(oldEnvVar); present { - err := flags.Set(f.Name, value) - if err != nil { - log.Infof("unable to configure flag %s using variable %s, err: %v", f.Name, oldEnvVar, err) - } - } - - newEnvVar := FlagNameToEnvVar(f.Name, "NB_") - - if value, present := os.LookupEnv(newEnvVar); present { - err := flags.Set(f.Name, value) - if err != nil { - log.Infof("unable to configure flag %s using variable %s, err: %v", f.Name, newEnvVar, err) - } - } - }) -} - -// FlagNameToEnvVar converts flag name to environment var name adding a prefix, -// replacing dashes and making all uppercase (e.g. setup-keys is converted to NB_SETUP_KEYS according to the input prefix) -func FlagNameToEnvVar(cmdFlag string, prefix string) string { - parsed := strings.ReplaceAll(cmdFlag, "-", "_") - upper := strings.ToUpper(parsed) - return prefix + upper -} - // DialClientGRPCServer returns client connection to the daemon server. func DialClientGRPCServer(ctx context.Context, addr string) (*grpc.ClientConn, error) { ctx, cancel := context.WithTimeout(ctx, time.Second*3) @@ -382,7 +350,7 @@ func migrateToNetbird(oldPath, newPath string) bool { } func getClient(cmd *cobra.Command) (*grpc.ClientConn, error) { - SetFlagsFromEnvVars(rootCmd) + util.SetFlagsFromEnvVars(rootCmd) cmd.SetOut(cmd.OutOrStdout()) conn, err := DialClientGRPCServer(cmd.Context(), daemonAddr) diff --git a/client/cmd/root_test.go b/client/cmd/root_test.go index 4cbbe8783ed..7487f639972 100644 --- a/client/cmd/root_test.go +++ b/client/cmd/root_test.go @@ -8,6 +8,7 @@ import ( "github.com/spf13/cobra" "github.com/netbirdio/netbird/client/iface" + "github.com/netbirdio/netbird/util" ) func TestInitCommands(t *testing.T) { @@ -45,7 +46,7 @@ func TestSetFlagsFromEnvVars(t *testing.T) { Long: "test", SilenceUsage: true, Run: func(cmd *cobra.Command, args []string) { - SetFlagsFromEnvVars(cmd) + util.SetFlagsFromEnvVars(cmd) }, } diff --git a/client/cmd/service_controller.go b/client/cmd/service_controller.go index 14a41e607b4..f0e1b95385e 100644 --- a/client/cmd/service_controller.go +++ b/client/cmd/service_controller.go @@ -103,8 +103,8 @@ func (p *program) Stop(srv service.Service) error { // Common setup for service control commands func setupServiceControlCommand(cmd *cobra.Command, ctx context.Context, cancel context.CancelFunc) (service.Service, error) { - SetFlagsFromEnvVars(rootCmd) - SetFlagsFromEnvVars(serviceCmd) + util.SetFlagsFromEnvVars(rootCmd) + util.SetFlagsFromEnvVars(serviceCmd) cmd.SetOut(cmd.OutOrStdout()) diff --git a/client/cmd/service_installer.go b/client/cmd/service_installer.go index ac22000bd71..1b69567f37c 100644 --- a/client/cmd/service_installer.go +++ b/client/cmd/service_installer.go @@ -20,8 +20,8 @@ var ErrGetServiceStatus = fmt.Errorf("failed to get service status") // Common service command setup func setupServiceCommand(cmd *cobra.Command) error { - SetFlagsFromEnvVars(rootCmd) - SetFlagsFromEnvVars(serviceCmd) + util.SetFlagsFromEnvVars(rootCmd) + util.SetFlagsFromEnvVars(serviceCmd) cmd.SetOut(cmd.OutOrStdout()) return handleRebrand(cmd) } diff --git a/client/cmd/ssh.go b/client/cmd/ssh.go index 5a52b37951b..7373b9b0ab5 100644 --- a/client/cmd/ssh.go +++ b/client/cmd/ssh.go @@ -42,8 +42,8 @@ var sshCmd = &cobra.Command{ }, Short: "connect to a remote SSH server", RunE: func(cmd *cobra.Command, args []string) error { - SetFlagsFromEnvVars(rootCmd) - SetFlagsFromEnvVars(cmd) + util.SetFlagsFromEnvVars(rootCmd) + util.SetFlagsFromEnvVars(cmd) cmd.SetOut(cmd.OutOrStdout()) diff --git a/client/cmd/status.go b/client/cmd/status.go index edc443f79bb..4f1c1a340e3 100644 --- a/client/cmd/status.go +++ b/client/cmd/status.go @@ -51,7 +51,7 @@ func init() { } func statusFunc(cmd *cobra.Command, args []string) error { - SetFlagsFromEnvVars(rootCmd) + util.SetFlagsFromEnvVars(rootCmd) cmd.SetOut(cmd.OutOrStdout()) diff --git a/client/cmd/up.go b/client/cmd/up.go index b62925e5e40..2d6cb38734b 100644 --- a/client/cmd/up.go +++ b/client/cmd/up.go @@ -84,8 +84,8 @@ func init() { } func upFunc(cmd *cobra.Command, args []string) error { - SetFlagsFromEnvVars(rootCmd) - SetFlagsFromEnvVars(cmd) + util.SetFlagsFromEnvVars(rootCmd) + util.SetFlagsFromEnvVars(cmd) cmd.SetOut(cmd.OutOrStdout()) diff --git a/relay/cmd/env.go b/relay/cmd/env.go deleted file mode 100644 index 3c15ebe1f3b..00000000000 --- a/relay/cmd/env.go +++ /dev/null @@ -1,35 +0,0 @@ -package cmd - -import ( - "os" - "strings" - - log "github.com/sirupsen/logrus" - "github.com/spf13/cobra" - "github.com/spf13/pflag" -) - -// setFlagsFromEnvVars reads and updates flag values from environment variables with prefix NB_ -func setFlagsFromEnvVars(cmd *cobra.Command) { - flags := cmd.PersistentFlags() - flags.VisitAll(func(f *pflag.Flag) { - newEnvVar := flagNameToEnvVar(f.Name, "NB_") - value, present := os.LookupEnv(newEnvVar) - if !present { - return - } - - err := flags.Set(f.Name, value) - if err != nil { - log.Infof("unable to configure flag %s using variable %s, err: %v", f.Name, newEnvVar, err) - } - }) -} - -// flagNameToEnvVar converts flag name to environment var name adding a prefix, -// replacing dashes and making all uppercase (e.g. setup-keys is converted to NB_SETUP_KEYS according to the input prefix) -func flagNameToEnvVar(cmdFlag string, prefix string) string { - parsed := strings.ReplaceAll(cmdFlag, "-", "_") - upper := strings.ToUpper(parsed) - return prefix + upper -} diff --git a/relay/cmd/root.go b/relay/cmd/root.go index c662dfbb77b..8a03bf2d2d5 100644 --- a/relay/cmd/root.go +++ b/relay/cmd/root.go @@ -88,7 +88,7 @@ func init() { rootCmd.PersistentFlags().StringVar(&cobraConfig.LogLevel, "log-level", "info", "log level") rootCmd.PersistentFlags().StringVar(&cobraConfig.LogFile, "log-file", "console", "log file") - setFlagsFromEnvVars(rootCmd) + util.SetFlagsFromEnvVars(rootCmd) } func Execute() error { diff --git a/signal/cmd/env.go b/signal/cmd/env.go deleted file mode 100644 index 3c15ebe1f3b..00000000000 --- a/signal/cmd/env.go +++ /dev/null @@ -1,35 +0,0 @@ -package cmd - -import ( - "os" - "strings" - - log "github.com/sirupsen/logrus" - "github.com/spf13/cobra" - "github.com/spf13/pflag" -) - -// setFlagsFromEnvVars reads and updates flag values from environment variables with prefix NB_ -func setFlagsFromEnvVars(cmd *cobra.Command) { - flags := cmd.PersistentFlags() - flags.VisitAll(func(f *pflag.Flag) { - newEnvVar := flagNameToEnvVar(f.Name, "NB_") - value, present := os.LookupEnv(newEnvVar) - if !present { - return - } - - err := flags.Set(f.Name, value) - if err != nil { - log.Infof("unable to configure flag %s using variable %s, err: %v", f.Name, newEnvVar, err) - } - }) -} - -// flagNameToEnvVar converts flag name to environment var name adding a prefix, -// replacing dashes and making all uppercase (e.g. setup-keys is converted to NB_SETUP_KEYS according to the input prefix) -func flagNameToEnvVar(cmdFlag string, prefix string) string { - parsed := strings.ReplaceAll(cmdFlag, "-", "_") - upper := strings.ToUpper(parsed) - return prefix + upper -} diff --git a/signal/cmd/run.go b/signal/cmd/run.go index 2e89b491ab8..a1819f9f273 100644 --- a/signal/cmd/run.go +++ b/signal/cmd/run.go @@ -303,5 +303,5 @@ func init() { runCmd.Flags().StringVar(&signalLetsencryptDomain, "letsencrypt-domain", "", "a domain to issue Let's Encrypt certificate for. Enables TLS using Let's Encrypt. Will fetch and renew certificate, and run the server with TLS") runCmd.Flags().StringVar(&signalCertFile, "cert-file", "", "Location of your SSL certificate. Can be used when you have an existing certificate and don't want a new certificate be generated automatically. If letsencrypt-domain is specified this property has no effect") runCmd.Flags().StringVar(&signalCertKey, "cert-key", "", "Location of your SSL certificate private key. Can be used when you have an existing certificate and don't want a new certificate be generated automatically. If letsencrypt-domain is specified this property has no effect") - setFlagsFromEnvVars(runCmd) + util.SetFlagsFromEnvVars(runCmd) } diff --git a/util/flags.go b/util/flags.go new file mode 100644 index 00000000000..3d9b8ca6271 --- /dev/null +++ b/util/flags.go @@ -0,0 +1,56 @@ +package util + +import ( + "os" + "path" + "strings" + + log "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "github.com/spf13/pflag" +) + +// setFlagsFromEnvVars reads and updates flag values from environment variables with prefix WT_ +func SetFlagsFromEnvVars(cmd *cobra.Command) { + // Fetch the credentials directory if it exists + credsDir, present := os.LookupEnv("CREDENTIALS_DIRECTORY") + + flags := cmd.PersistentFlags() + flags.VisitAll(func(f *pflag.Flag) { + name := flagNameToUpper(f.Name) + + // Try to get the value from the credential directory + if present { + data, e := os.ReadFile(path.Join(credsDir, name)) + + if e == nil { + err := flags.Set(f.Name, strings.TrimSuffix(string(data), "\n")) + + if err != nil { + log.Infof("unable to configure flag %s using credential %s, err: %v", f.Name, name, err) + } else { + return + } + } + } + + // Fallback to env variable, which is constructed by adding the required prefix + // E.g. SETUP_KEYS -> NB_SETUP_KEYS + envName := "NB_" + name + + if value, varPresent := os.LookupEnv(envName); varPresent { + err := flags.Set(f.Name, value) + + if err != nil { + log.Infof("unable to configure flag %s using variable %s, err: %v", f.Name, envName, err) + } + } + }) +} + +// flagNameToUpper converts a flag name to its corresponding base env name +// replacing dashes by underscores and making the result uppercase +// E.g. setup-keys -> SETUP_KEYS +func flagNameToUpper(cmdFlag string) string { + return strings.ToUpper(strings.ReplaceAll(cmdFlag, "-", "_")) +}