From d5e2df49db91aefa9a0dd14a621c8436f9d6de60 Mon Sep 17 00:00:00 2001 From: Daniel Del Rio Figueira Date: Fri, 29 Nov 2024 10:16:33 +0100 Subject: [PATCH 1/4] Added support for http client configuration via command arguments --- cmd/root.go | 8 ++++++++ cmd/verify.go | 6 +++++- util/client.go | 6 +++++- util/http.go | 44 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 util/http.go diff --git a/cmd/root.go b/cmd/root.go index b1274e1..74048df 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -60,6 +60,10 @@ func init() { rootCmd.PersistentFlags().Uint("mfaRetrys", 3, "How often to retry TOTP Auth, only used in nointeractive modes") rootCmd.PersistentFlags().Duration("mfaDelay", time.Second*10, "Delay between MFA Attempts, only used in noninteractive modes") + rootCmd.PersistentFlags().Bool("tlsSkipVerify", false, "Allow servers with self-signed certificates") + rootCmd.PersistentFlags().String("tlsClientPrivateKey", "", "Client private key for mtls") + rootCmd.PersistentFlags().String("tlsClientCert", "", "Client certificate for mtls") + viper.BindPFlag("debug", rootCmd.PersistentFlags().Lookup("debug")) viper.BindPFlag("timeout", rootCmd.PersistentFlags().Lookup("timeout")) viper.BindPFlag("serverAddress", rootCmd.PersistentFlags().Lookup("serverAddress")) @@ -72,6 +76,10 @@ func init() { viper.BindPFlag("mfaTotpOffset", rootCmd.PersistentFlags().Lookup("mfaTotpOffset")) viper.BindPFlag("mfaRetrys", rootCmd.PersistentFlags().Lookup("mfaRetrys")) viper.BindPFlag("mfaDelay", rootCmd.PersistentFlags().Lookup("mfaDelay")) + + viper.BindPFlag("tlsSkipVerify", rootCmd.PersistentFlags().Lookup("tlsSkipVerify")) + viper.BindPFlag("tlsClientCert", rootCmd.PersistentFlags().Lookup("tlsClientCert")) + viper.BindPFlag("tlsClientPrivateKey", rootCmd.PersistentFlags().Lookup("tlsClientPrivateKey")) } // initConfig reads in config file and ENV variables if set. diff --git a/cmd/verify.go b/cmd/verify.go index 0a12c7e..e11d41a 100644 --- a/cmd/verify.go +++ b/cmd/verify.go @@ -41,7 +41,11 @@ var verifyCMD = &cobra.Command{ fmt.Println() } - client, err := api.NewClient(nil, "", serverAddress, userPrivateKey, userPassword) + httpClient, err := util.GetHttpClient() + if err != nil { + return err + } + client, err := api.NewClient(httpClient, "", serverAddress, userPrivateKey, userPassword) if err != nil { return fmt.Errorf("Creating Client: %w", err) } diff --git a/util/client.go b/util/client.go index 194e500..060f985 100644 --- a/util/client.go +++ b/util/client.go @@ -65,7 +65,11 @@ func GetClient(ctx context.Context) (*api.Client, error) { fmt.Println() } - client, err := api.NewClient(nil, "", serverAddress, userPrivateKey, userPassword) + httpClient, err := GetHttpClient() + if err != nil { + return nil, err + } + client, err := api.NewClient(httpClient, "", serverAddress, userPrivateKey, userPassword) if err != nil { return nil, fmt.Errorf("Creating Client: %w", err) } diff --git a/util/http.go b/util/http.go new file mode 100644 index 0000000..b244a0c --- /dev/null +++ b/util/http.go @@ -0,0 +1,44 @@ +package util + +import ( + "crypto/tls" + "fmt" + "net/http" + + "github.com/spf13/viper" +) + +func GetClientCertificate() (tls.Certificate, error) { + cert := viper.GetString("tlsClientCert") + certExists := cert != "" + key := viper.GetString("tlsClientPrivateKey") + keyExists := key != "" + if !certExists && !keyExists { + return tls.Certificate{}, nil + } + if certExists && !keyExists { + return tls.Certificate{}, fmt.Errorf("Client TLS private key is empty, but client TLS cert was sent.") + } + if !certExists && keyExists { + return tls.Certificate{}, fmt.Errorf("Client TLS cert is empty, but client TLS private key was sent.") + } + return tls.LoadX509KeyPair("client.cert", "client-key.pem") +} + +func GetHttpClient() (*http.Client, error) { + tlsSkipVerify := viper.GetBool("tlsSkipVerify") + cert, err := GetClientCertificate() + if err != nil { + return nil, err + } + httpClient := http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{ + Certificates: []tls.Certificate{cert}, + InsecureSkipVerify: tlsSkipVerify, + }, + }, + } + + return &httpClient, nil +} From 72cfd79b77256209ea1419abac75191b4474d574 Mon Sep 17 00:00:00 2001 From: Daniel Del Rio Figueira Date: Mon, 2 Dec 2024 09:26:25 +0100 Subject: [PATCH 2/4] Removed hardcoded paths and fixed typo --- util/http.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/util/http.go b/util/http.go index b244a0c..9ab238f 100644 --- a/util/http.go +++ b/util/http.go @@ -17,12 +17,12 @@ func GetClientCertificate() (tls.Certificate, error) { return tls.Certificate{}, nil } if certExists && !keyExists { - return tls.Certificate{}, fmt.Errorf("Client TLS private key is empty, but client TLS cert was sent.") + return tls.Certificate{}, fmt.Errorf("Client TLS private key is empty, but client TLS cert was set.") } if !certExists && keyExists { - return tls.Certificate{}, fmt.Errorf("Client TLS cert is empty, but client TLS private key was sent.") + return tls.Certificate{}, fmt.Errorf("Client TLS cert is empty, but client TLS private key was set.") } - return tls.LoadX509KeyPair("client.cert", "client-key.pem") + return tls.LoadX509KeyPair(cert, key) } func GetHttpClient() (*http.Client, error) { From 0273cee2ba34c23584c05275bc202232178dd393 Mon Sep 17 00:00:00 2001 From: Daniel Del Rio Figueira Date: Mon, 2 Dec 2024 09:27:02 +0100 Subject: [PATCH 3/4] Added File suffix on tlsClient command flags --- cmd/root.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/root.go b/cmd/root.go index 74048df..d84cbb6 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -61,8 +61,8 @@ func init() { rootCmd.PersistentFlags().Duration("mfaDelay", time.Second*10, "Delay between MFA Attempts, only used in noninteractive modes") rootCmd.PersistentFlags().Bool("tlsSkipVerify", false, "Allow servers with self-signed certificates") - rootCmd.PersistentFlags().String("tlsClientPrivateKey", "", "Client private key for mtls") - rootCmd.PersistentFlags().String("tlsClientCert", "", "Client certificate for mtls") + rootCmd.PersistentFlags().String("tlsClientPrivateKeyFile", "", "Client private key for mtls") + rootCmd.PersistentFlags().String("tlsClientCertFile", "", "Client certificate for mtls") viper.BindPFlag("debug", rootCmd.PersistentFlags().Lookup("debug")) viper.BindPFlag("timeout", rootCmd.PersistentFlags().Lookup("timeout")) From 78ed21f62b92b080d8f8f23309f1d33e5cb7b57f Mon Sep 17 00:00:00 2001 From: Daniel Del Rio Figueira Date: Wed, 4 Dec 2024 00:39:11 +0100 Subject: [PATCH 4/4] Added flags for passing client cert and client private key as file contents instead of paths --- cmd/root.go | 45 +++++++++++++++++++++++++++++++++------------ util/http.go | 2 +- 2 files changed, 34 insertions(+), 13 deletions(-) diff --git a/cmd/root.go b/cmd/root.go index d84cbb6..212ad88 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -2,7 +2,6 @@ package cmd import ( "fmt" - "io/ioutil" "os" "path/filepath" "time" @@ -61,8 +60,10 @@ func init() { rootCmd.PersistentFlags().Duration("mfaDelay", time.Second*10, "Delay between MFA Attempts, only used in noninteractive modes") rootCmd.PersistentFlags().Bool("tlsSkipVerify", false, "Allow servers with self-signed certificates") - rootCmd.PersistentFlags().String("tlsClientPrivateKeyFile", "", "Client private key for mtls") - rootCmd.PersistentFlags().String("tlsClientCertFile", "", "Client certificate for mtls") + rootCmd.PersistentFlags().String("tlsClientPrivateKeyFile", "", "Client private key path for mtls") + rootCmd.PersistentFlags().String("tlsClientCertFile", "", "Client certificate path for mtls") + rootCmd.PersistentFlags().String("tlsClientPrivateKey", "", "Client private key for mtls") + rootCmd.PersistentFlags().String("tlsClientCert", "", "Client certificate for mtls") viper.BindPFlag("debug", rootCmd.PersistentFlags().Lookup("debug")) viper.BindPFlag("timeout", rootCmd.PersistentFlags().Lookup("timeout")) @@ -82,6 +83,18 @@ func init() { viper.BindPFlag("tlsClientPrivateKey", rootCmd.PersistentFlags().Lookup("tlsClientPrivateKey")) } +func fileToContent(file, contentFlag string) { + if viper.GetBool("debug") { + fmt.Fprintln(os.Stderr, "Loading file:", file) + } + content, err := os.ReadFile(file) + if err != nil { + fmt.Fprintln(os.Stderr, "Error Loading File: ", err) + os.Exit(1) + } + viper.Set(contentFlag, string(content)) +} + // initConfig reads in config file and ENV variables if set. func initConfig() { if cfgFile != "" { @@ -115,18 +128,26 @@ func initConfig() { // Read in Private Key from File if userprivatekeyfile is set userprivatekeyfile, err := rootCmd.PersistentFlags().GetString("userPrivateKeyFile") if err == nil && userprivatekeyfile != "" { - if viper.GetBool("debug") { - fmt.Fprintln(os.Stderr, "Loading Private Key from File:", userprivatekeyfile) - } - content, err := ioutil.ReadFile(userprivatekeyfile) - if err != nil { - fmt.Fprintln(os.Stderr, "Error Loading Private Key from File: ", err) - os.Exit(1) - } - viper.Set("userprivatekey", string(content)) + fileToContent(userprivatekeyfile, "userPrivateKey") } else if err != nil && viper.GetBool("debug") { fmt.Fprintln(os.Stderr, "Getting Private Key File Flag:", err) } + + // Read in Client Certificate Private Key from File if tlsClientPrivateKeyFile is set + tlsclientprivatekeyfile, err := rootCmd.PersistentFlags().GetString("tlsClientPrivateKeyFile") + if err == nil && tlsclientprivatekeyfile != "" { + fileToContent(tlsclientprivatekeyfile, "tlsClientPrivateKey") + } else if err != nil && viper.GetBool("debug") { + fmt.Fprintln(os.Stderr, "Getting Client Certificate Private key File Flag:", err) + } + + // Read in Client Certificate from File if tlsClientCertFile is set + tlsclientcertfile, err := rootCmd.PersistentFlags().GetString("tlsClientCertFile") + if err == nil && tlsclientcertfile != "" { + fileToContent(tlsclientcertfile, "tlsClientCert") + } else if err != nil && viper.GetBool("debug") { + fmt.Fprintln(os.Stderr, "Getting Client Certificate File Flag:", err) + } } func SetVersionInfo(version, commit, date string, dirty bool) { diff --git a/util/http.go b/util/http.go index 9ab238f..f9943f9 100644 --- a/util/http.go +++ b/util/http.go @@ -22,7 +22,7 @@ func GetClientCertificate() (tls.Certificate, error) { if !certExists && keyExists { return tls.Certificate{}, fmt.Errorf("Client TLS cert is empty, but client TLS private key was set.") } - return tls.LoadX509KeyPair(cert, key) + return tls.X509KeyPair([]byte(cert), []byte(key)) } func GetHttpClient() (*http.Client, error) {