From 7c45a44c6a8adb41e1342745e8dd99d7268f3bff Mon Sep 17 00:00:00 2001 From: Ashley Davis Date: Fri, 25 Apr 2025 13:55:03 +0100 Subject: [PATCH 1/2] WIP: allow users to select machine hub mode Signed-off-by: Ashley Davis --- pkg/agent/config.go | 74 ++++++++++++++++++++++++++++++++++++++++----- pkg/agent/run.go | 41 ++++++++++++++++++------- 2 files changed, 96 insertions(+), 19 deletions(-) diff --git a/pkg/agent/config.go b/pkg/agent/config.go index fed4c799..1a708ab1 100644 --- a/pkg/agent/config.go +++ b/pkg/agent/config.go @@ -39,7 +39,7 @@ type Config struct { // https://preflight.jetstack.io in Jetstack Secure OAuth and Jetstack // Secure API Token modes, and https://api.venafi.cloud in Venafi Cloud Key // Pair Service Account mode. It is ignored in Venafi Cloud VenafiConnection - // mode. + // mode and in MachineHub mode. Server string `yaml:"server"` // OrganizationID is only used in Jetstack Secure OAuth and Jetstack Secure @@ -62,6 +62,9 @@ type Config struct { ExcludeAnnotationKeysRegex []string `yaml:"exclude-annotation-keys-regex"` // Skips label keys that match the given set of regular expressions. ExcludeLabelKeysRegex []string `yaml:"exclude-label-keys-regex"` + + // MachineHub holds config specific to MachineHub mode + MachineHub MachineHubConfig `yaml:"machineHub"` } type Endpoint struct { @@ -89,6 +92,32 @@ type VenafiCloudConfig struct { UploadPath string `yaml:"upload_path,omitempty"` } +// MachineHubConfig holds configuration values specific to the CyberArk Machine Hub integration +type MachineHubConfig struct { + // Subdomain is the subdomain indicating where data should be pushed. Used for + // querying the Service Discovery Service to discover the Identity API URL + Subdomain string `yaml:"subdomain"` + + // CredentialsSecretName is the name of a Kubernetes Secret in the same namespace as + // the agent, which will be watched for a username and password to send to CyberArk + // Identity for authentication. + CredentialsSecretName string `yaml:"credentialsSecretName"` +} + +func (mhc MachineHubConfig) Validate() error { + var errs []error + + if mhc.Subdomain == "" { + errs = append(errs, fmt.Errorf("subdomain must not be empty in MachineHub mode")) + } + + if mhc.CredentialsSecretName == "" { + errs = append(errs, fmt.Errorf("credentialsSecretName must not be empty in MachineHub mode")) + } + + return errors.Join(errs...) +} + type AgentCmdFlags struct { // ConfigFilePath (--config-file, -c) is the path to the agent configuration // YAML file. @@ -103,6 +132,9 @@ type AgentCmdFlags struct { // --credentials-file. VenafiCloudMode bool + // MachineHubMode configures the agent to send data to CyberArk Machine Hub + MachineHubMode bool + // ClientID (--client-id) is the clientID in case of Venafi Cloud Key Pair // Service Account mode. ClientID string @@ -305,6 +337,8 @@ func InitAgentCmdFlags(c *cobra.Command, cfg *AgentCmdFlags) { } +// AuthMode controls how to authenticate to TLSPK / Jetstack Secure. Only one AuthMode +// may be provided if using using those backends. type AuthMode string const ( @@ -312,6 +346,8 @@ const ( JetstackSecureAPIToken AuthMode = "Jetstack Secure API Token" VenafiCloudKeypair AuthMode = "Venafi Cloud Key Pair Service Account" VenafiCloudVenafiConnection AuthMode = "Venafi Cloud VenafiConnection" + + NoTLSPK AuthMode = "MachineHub only" ) // The command-line flags and the config file are combined into this struct by @@ -351,6 +387,11 @@ type CombinedConfig struct { // Only used for testing purposes. OutputPath string InputPath string + + // MachineHub-related settings + MachineHubMode bool + + MachineHub MachineHubConfig } // ValidateAndCombineConfig combines and validates the input configuration with @@ -366,6 +407,16 @@ func ValidateAndCombineConfig(log logr.Logger, cfg Config, flags AgentCmdFlags) res := CombinedConfig{} var errs error + if flags.MachineHubMode { + err := cfg.MachineHub.Validate() + if err != nil { + return CombinedConfig{}, nil, fmt.Errorf("invalid MachineHub config provided: %w", err) + } + + res.MachineHubMode = true + res.MachineHubConfig = cfg.MachineHubConfig + } + { var ( mode AuthMode @@ -396,19 +447,26 @@ func ValidateAndCombineConfig(log logr.Logger, cfg Config, flags AgentCmdFlags) mode = JetstackSecureOAuth reason = "--credentials-file was specified without --venafi-cloud" default: - return CombinedConfig{}, nil, fmt.Errorf("no auth mode specified. You can use one of four auth modes:\n" + - " - Use (--venafi-cloud with --credentials-file) or (--client-id with --private-key-path) to use the " + string(VenafiCloudKeypair) + " mode.\n" + - " - Use --venafi-connection for the " + string(VenafiCloudVenafiConnection) + " mode.\n" + - " - Use --credentials-file alone if you want to use the " + string(JetstackSecureOAuth) + " mode.\n" + - " - Use --api-token if you want to use the " + string(JetstackSecureAPIToken) + " mode.\n") + if !flags.MachineHubMode { + return CombinedConfig{}, nil, fmt.Errorf("no auth mode specified and MachineHub mode is disabled. You can use one of four auth modes:\n" + + " - Use (--venafi-cloud with --credentials-file) or (--client-id with --private-key-path) to use the " + string(VenafiCloudKeypair) + " mode.\n" + + " - Use --venafi-connection for the " + string(VenafiCloudVenafiConnection) + " mode.\n" + + " - Use --credentials-file alone if you want to use the " + string(JetstackSecureOAuth) + " mode.\n" + + " - Use --api-token if you want to use the " + string(JetstackSecureAPIToken) + " mode.\n") + } + + mode = NoTLSPK + reason = "only MachineHub has been configured as a backend" } + res.AuthMode = mode keysAndValues = append(keysAndValues, "mode", mode, "reason", reason) log.V(logs.Debug).Info("Authentication mode", keysAndValues...) } // Validation and defaulting of `server` and the deprecated `endpoint.path`. - { + if res.AuthMode != NoTLSPK { + // Only relevant if using TLSPK backends hasEndpointField := cfg.Endpoint.Host != "" && cfg.Endpoint.Path != "" hasServerField := cfg.Server != "" var server string @@ -493,7 +551,7 @@ func ValidateAndCombineConfig(log logr.Logger, cfg Config, flags AgentCmdFlags) } // Validation of `cluster_id` and `organization_id`. - { + if res.AuthMode != NoTLSPK { var clusterID string var organizationID string // Only used by the old jetstack-secure mode. switch { diff --git a/pkg/agent/run.go b/pkg/agent/run.go index 495cff08..566f0e9b 100644 --- a/pkg/agent/run.go +++ b/pkg/agent/run.go @@ -327,19 +327,38 @@ func gatherAndOutputData(ctx context.Context, eventf Eventf, config CombinedConf } log.Info("Data saved to local file", "outputPath", config.OutputPath) } else { - backOff := backoff.NewExponentialBackOff() - backOff.InitialInterval = 30 * time.Second - backOff.MaxInterval = 3 * time.Minute - backOff.MaxElapsedTime = config.BackoffMaxTime - post := func() error { - return postData(klog.NewContext(ctx, log), config, preflightClient, readings) - } - err := backoff.RetryNotify(post, backOff, func(err error, t time.Duration) { + group, gctx := errgroup.WithContext(ctx) + + backOffCfg := backoff.NewExponentialBackOff() + backOffCfg.InitialInterval = 30 * time.Second + backOffCfg.MaxInterval = 3 * time.Minute + backOffCfg.MaxElapsedTime = config.BackoffMaxTime + + notifyFn := func(err error, t time.Duration) { eventf("Warning", "PushingErr", "retrying in %v after error: %s", t, err) log.Info("Warning: PushingErr: retrying", "in", t, "reason", err) - }) - if err != nil { - return fmt.Errorf("Exiting due to fatal error uploading: %v", err) + } + + if cfg.MachineHubMode { + post := func() error { + log.Info("machine hub mode not yet implemented") + return nil + } + + group.Go(backoff.RetryNotify(post, backOffCfg, notifyFn)) + } + + if cfg.AuthMode != NoTLSPK { + post := func() error { + return postData(klog.NewContext(ctx, log), config, preflightClient, readings) + } + + group.Go(backoff.RetryNotify(post, backOffCfg, notifyFn)) + } + + groupErr := group.Wait() + if groupErr != nil { + return fmt.Errorf("got a fatal error from one or more upload actions: %s", groupErr) } } return nil From c6bbefb399382e28d29062596b4622252b26ef5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Valais?= Date: Wed, 14 May 2025 10:56:08 +0200 Subject: [PATCH 2/2] machinehub: add the --machine-hub flag and add tests --- pkg/agent/config.go | 135 ++++++++++++++++++++++----------------- pkg/agent/config_test.go | 67 ++++++++++++++----- pkg/agent/run.go | 12 ++-- 3 files changed, 136 insertions(+), 78 deletions(-) diff --git a/pkg/agent/config.go b/pkg/agent/config.go index 1a708ab1..df873b56 100644 --- a/pkg/agent/config.go +++ b/pkg/agent/config.go @@ -1,6 +1,7 @@ package agent import ( + "errors" "fmt" "io" "net/url" @@ -10,7 +11,6 @@ import ( "github.com/go-logr/logr" "github.com/hashicorp/go-multierror" - "github.com/pkg/errors" "github.com/spf13/cobra" "gopkg.in/yaml.v3" "k8s.io/client-go/rest" @@ -63,7 +63,7 @@ type Config struct { // Skips label keys that match the given set of regular expressions. ExcludeLabelKeysRegex []string `yaml:"exclude-label-keys-regex"` - // MachineHub holds config specific to MachineHub mode + // MachineHub holds config specific to MachineHub mode. MachineHub MachineHubConfig `yaml:"machineHub"` } @@ -94,13 +94,14 @@ type VenafiCloudConfig struct { // MachineHubConfig holds configuration values specific to the CyberArk Machine Hub integration type MachineHubConfig struct { - // Subdomain is the subdomain indicating where data should be pushed. Used for - // querying the Service Discovery Service to discover the Identity API URL + // Subdomain is the subdomain indicating where data should be pushed. Used + // for querying the Service Discovery Service to discover the Identity API + // URL. Subdomain string `yaml:"subdomain"` - // CredentialsSecretName is the name of a Kubernetes Secret in the same namespace as - // the agent, which will be watched for a username and password to send to CyberArk - // Identity for authentication. + // CredentialsSecretName is the name of a Kubernetes Secret in the same + // namespace as the agent, which will be watched for a username and password + // to send to CyberArk Identity for authentication. CredentialsSecretName string `yaml:"credentialsSecretName"` } @@ -132,7 +133,7 @@ type AgentCmdFlags struct { // --credentials-file. VenafiCloudMode bool - // MachineHubMode configures the agent to send data to CyberArk Machine Hub + // MachineHubMode configures the agent to send data to CyberArk Machine Hub. MachineHubMode bool // ClientID (--client-id) is the clientID in case of Venafi Cloud Key Pair @@ -335,34 +336,44 @@ func InitAgentCmdFlags(c *cobra.Command, cfg *AgentCmdFlags) { ) c.PersistentFlags().MarkDeprecated("disable-compression", "no longer has an effect") + c.PersistentFlags().BoolVar( + &cfg.MachineHubMode, + "machine-hub", + false, + "Enables MachineHub mode. The agent will push data to CyberArk MachineHub. Can be used in conjunction with --venafi-cloud.", + ) + } -// AuthMode controls how to authenticate to TLSPK / Jetstack Secure. Only one AuthMode -// may be provided if using using those backends. -type AuthMode string +// TLSPKMode controls how to authenticate to TLSPK / Jetstack Secure. Only one +// TLSPKMode may be provided if using using those backends. +type TLSPKMode string const ( - JetstackSecureOAuth AuthMode = "Jetstack Secure OAuth" - JetstackSecureAPIToken AuthMode = "Jetstack Secure API Token" - VenafiCloudKeypair AuthMode = "Venafi Cloud Key Pair Service Account" - VenafiCloudVenafiConnection AuthMode = "Venafi Cloud VenafiConnection" - - NoTLSPK AuthMode = "MachineHub only" + JetstackSecureOAuth TLSPKMode = "Jetstack Secure OAuth" + JetstackSecureAPIToken TLSPKMode = "Jetstack Secure API Token" + VenafiCloudKeypair TLSPKMode = "Venafi Cloud Key Pair Service Account" + VenafiCloudVenafiConnection TLSPKMode = "Venafi Cloud VenafiConnection" + + // It is possible to push to both MachineHub and TLSPK. With this mode, the + // agent will only push to MachineHub and not to TLSPK. + Off TLSPKMode = "MachineHub only" ) // The command-line flags and the config file are combined into this struct by // ValidateAndCombineConfig. type CombinedConfig struct { - AuthMode AuthMode - - // Used by all modes. - ClusterID string DataGatherers []DataGatherer Period time.Duration BackoffMaxTime time.Duration + InstallNS string StrictMode bool OneShot bool - InstallNS string + + TLSPKMode TLSPKMode + + // Used by all TLSPK modes. + ClusterID string // Used by JetstackSecureOAuth, JetstackSecureAPIToken, and // VenafiCloudKeypair. Ignored in VenafiCloudVenafiConnection mode. @@ -388,10 +399,10 @@ type CombinedConfig struct { OutputPath string InputPath string - // MachineHub-related settings - MachineHubMode bool - - MachineHub MachineHubConfig + // MachineHub-related settings. + MachineHubMode bool + MachineHubSubdomain string + MachineHubCredentialsSecretName string } // ValidateAndCombineConfig combines and validates the input configuration with @@ -414,12 +425,16 @@ func ValidateAndCombineConfig(log logr.Logger, cfg Config, flags AgentCmdFlags) } res.MachineHubMode = true - res.MachineHubConfig = cfg.MachineHubConfig + res.MachineHubSubdomain = cfg.MachineHub.Subdomain + res.MachineHubCredentialsSecretName = cfg.MachineHub.CredentialsSecretName + + keysAndValues := []any{"credentialsSecretName", res.MachineHubCredentialsSecretName} + log.V(logs.Info).Info("Will push to CyberArk MachineHub using a username and password loaded from a Kubernetes Secret", keysAndValues...) } { var ( - mode AuthMode + mode TLSPKMode reason string keysAndValues []any ) @@ -448,24 +463,28 @@ func ValidateAndCombineConfig(log logr.Logger, cfg Config, flags AgentCmdFlags) reason = "--credentials-file was specified without --venafi-cloud" default: if !flags.MachineHubMode { - return CombinedConfig{}, nil, fmt.Errorf("no auth mode specified and MachineHub mode is disabled. You can use one of four auth modes:\n" + + return CombinedConfig{}, nil, fmt.Errorf("no TLSPK mode specified and MachineHub mode is disabled. You must either enable the MachineHub mode (using --machine-hub), or enable one of the TLSPK modes.\n" + + "To enable one of the TLSPK modes, you can:\n" + " - Use (--venafi-cloud with --credentials-file) or (--client-id with --private-key-path) to use the " + string(VenafiCloudKeypair) + " mode.\n" + " - Use --venafi-connection for the " + string(VenafiCloudVenafiConnection) + " mode.\n" + " - Use --credentials-file alone if you want to use the " + string(JetstackSecureOAuth) + " mode.\n" + - " - Use --api-token if you want to use the " + string(JetstackSecureAPIToken) + " mode.\n") + " - Use --api-token if you want to use the " + string(JetstackSecureAPIToken) + " mode.\n" + + "Note that it is possible to use one of the TLSPK modes along with the MachineHub mode (--machine-hub).") } - mode = NoTLSPK - reason = "only MachineHub has been configured as a backend" + mode = Off } - res.AuthMode = mode keysAndValues = append(keysAndValues, "mode", mode, "reason", reason) - log.V(logs.Debug).Info("Authentication mode", keysAndValues...) + if mode != Off { + log.V(logs.Debug).Info("Configured to push to Venafi", keysAndValues...) + } + + res.TLSPKMode = mode } // Validation and defaulting of `server` and the deprecated `endpoint.path`. - if res.AuthMode != NoTLSPK { + if res.TLSPKMode != Off { // Only relevant if using TLSPK backends hasEndpointField := cfg.Endpoint.Host != "" && cfg.Endpoint.Path != "" hasServerField := cfg.Server != "" @@ -488,7 +507,7 @@ func ValidateAndCombineConfig(log logr.Logger, cfg Config, flags AgentCmdFlags) endpointPath = cfg.Endpoint.Path case !hasServerField && !hasEndpointField: server = "https://preflight.jetstack.io" - if res.AuthMode == VenafiCloudKeypair { + if res.TLSPKMode == VenafiCloudKeypair { // The VenafiCloudVenafiConnection mode doesn't need a server. server = client.VenafiCloudProdURL } @@ -497,7 +516,7 @@ func ValidateAndCombineConfig(log logr.Logger, cfg Config, flags AgentCmdFlags) if urlErr != nil || url.Hostname() == "" { errs = multierror.Append(errs, fmt.Errorf("server %q is not a valid URL", server)) } - if res.AuthMode == VenafiCloudVenafiConnection && server != "" { + if res.TLSPKMode == VenafiCloudVenafiConnection && server != "" { log.Info(fmt.Sprintf("ignoring the server field specified in the config file. In %s mode, this field is not needed.", VenafiCloudVenafiConnection)) server = "" } @@ -509,9 +528,9 @@ func ValidateAndCombineConfig(log logr.Logger, cfg Config, flags AgentCmdFlags) { var uploadPath string switch { - case res.AuthMode == VenafiCloudKeypair: + case res.TLSPKMode == VenafiCloudKeypair: if cfg.VenafiCloud == nil || cfg.VenafiCloud.UploadPath == "" { - errs = multierror.Append(errs, fmt.Errorf("the venafi-cloud.upload_path field is required when using the %s mode", res.AuthMode)) + errs = multierror.Append(errs, fmt.Errorf("the venafi-cloud.upload_path field is required when using the %s mode", res.TLSPKMode)) break // Skip to the end of the switch statement. } _, urlErr := url.Parse(cfg.VenafiCloud.UploadPath) @@ -521,14 +540,14 @@ func ValidateAndCombineConfig(log logr.Logger, cfg Config, flags AgentCmdFlags) } uploadPath = cfg.VenafiCloud.UploadPath - case res.AuthMode == VenafiCloudVenafiConnection: + case res.TLSPKMode == VenafiCloudVenafiConnection: // The venafi-cloud.upload_path was initially meant to let users // configure HTTP proxies, but it has never been used since HTTP // proxies don't rewrite paths. Thus, we've disabled the ability to // change this value with the new --venafi-connection flag, and this // field is simply ignored. if cfg.VenafiCloud != nil && cfg.VenafiCloud.UploadPath != "" { - log.Info(fmt.Sprintf(`ignoring the venafi-cloud.upload_path field in the config file. In %s mode, this field is not needed.`, res.AuthMode)) + log.Info(fmt.Sprintf(`ignoring the venafi-cloud.upload_path field in the config file. In %s mode, this field is not needed.`, res.TLSPKMode)) } uploadPath = "" } @@ -546,26 +565,26 @@ func ValidateAndCombineConfig(log logr.Logger, cfg Config, flags AgentCmdFlags) // https://venafi.atlassian.net/browse/VC-35385 is done. { if cfg.VenafiCloud != nil && cfg.VenafiCloud.UploaderID != "" { - log.Info(fmt.Sprintf(`ignoring the venafi-cloud.uploader_id field in the config file. This field is not needed in %s mode.`, res.AuthMode)) + log.Info(fmt.Sprintf(`ignoring the venafi-cloud.uploader_id field in the config file. This field is not needed in %s mode.`, res.TLSPKMode)) } } // Validation of `cluster_id` and `organization_id`. - if res.AuthMode != NoTLSPK { + if res.TLSPKMode != Off { var clusterID string var organizationID string // Only used by the old jetstack-secure mode. switch { - case res.AuthMode == VenafiCloudKeypair: + case res.TLSPKMode == VenafiCloudKeypair: if cfg.ClusterID == "" { - errs = multierror.Append(errs, fmt.Errorf("cluster_id is required in %s mode", res.AuthMode)) + errs = multierror.Append(errs, fmt.Errorf("cluster_id is required in %s mode", res.TLSPKMode)) } clusterID = cfg.ClusterID - case res.AuthMode == VenafiCloudVenafiConnection: + case res.TLSPKMode == VenafiCloudVenafiConnection: if cfg.ClusterID == "" { - errs = multierror.Append(errs, fmt.Errorf("cluster_id is required in %s mode", res.AuthMode)) + errs = multierror.Append(errs, fmt.Errorf("cluster_id is required in %s mode", res.TLSPKMode)) } clusterID = cfg.ClusterID - case res.AuthMode == JetstackSecureOAuth || res.AuthMode == JetstackSecureAPIToken: + case res.TLSPKMode == JetstackSecureOAuth || res.TLSPKMode == JetstackSecureAPIToken: if cfg.OrganizationID == "" { errs = multierror.Append(errs, fmt.Errorf("organization_id is required")) } @@ -625,7 +644,7 @@ func ValidateAndCombineConfig(log logr.Logger, cfg Config, flags AgentCmdFlags) res.InstallNS = installNS // Validation of --venafi-connection and --venafi-connection-namespace. - if res.AuthMode == VenafiCloudVenafiConnection { + if res.TLSPKMode == VenafiCloudVenafiConnection { res.VenConnName = flags.VenConnName var venConnNS string = flags.VenConnNS if flags.VenConnNS == "" { @@ -692,7 +711,7 @@ func validateCredsAndCreateClient(log logr.Logger, flagCredentialsPath, flagClie var preflightClient client.Client metadata := &api.AgentMetadata{Version: version.PreflightVersion, ClusterID: cfg.ClusterID} switch { - case cfg.AuthMode == JetstackSecureOAuth: + case cfg.TLSPKMode == JetstackSecureOAuth: // Note that there are no command line flags to configure the // JetstackSecureOAuth mode. credsBytes, err := readCredentialsFile(flagCredentialsPath) @@ -711,7 +730,7 @@ func validateCredsAndCreateClient(log logr.Logger, flagCredentialsPath, flagClie if err != nil { errs = multierror.Append(errs, err) } - case cfg.AuthMode == VenafiCloudKeypair: + case cfg.TLSPKMode == VenafiCloudKeypair: var creds client.Credentials if flagClientID != "" && flagCredentialsPath != "" { @@ -754,7 +773,7 @@ func validateCredsAndCreateClient(log logr.Logger, flagCredentialsPath, flagClie if err != nil { errs = multierror.Append(errs, err) } - case cfg.AuthMode == VenafiCloudVenafiConnection: + case cfg.TLSPKMode == VenafiCloudVenafiConnection: var restCfg *rest.Config restCfg, err := kubeconfig.LoadRESTConfig("") if err != nil { @@ -766,18 +785,20 @@ func validateCredsAndCreateClient(log logr.Logger, flagCredentialsPath, flagClie if err != nil { errs = multierror.Append(errs, err) } - case cfg.AuthMode == JetstackSecureAPIToken: + case cfg.TLSPKMode == JetstackSecureAPIToken: var err error preflightClient, err = client.NewAPITokenClient(metadata, flagAPIToken, cfg.Server) if err != nil { errs = multierror.Append(errs, err) } + case cfg.TLSPKMode == Off: + // No client needed in this mode. default: - panic(fmt.Errorf("programmer mistake: auth mode not implemented: %s", cfg.AuthMode)) + panic(fmt.Errorf("programmer mistake: auth mode not implemented: %s", cfg.TLSPKMode)) } if errs != nil { - return nil, fmt.Errorf("failed loading config using the %s mode: %w", cfg.AuthMode, errs) + return nil, fmt.Errorf("failed loading config using the %s mode: %w", cfg.TLSPKMode, errs) } return preflightClient, nil @@ -814,7 +835,7 @@ func createCredentialClient(log logr.Logger, credentials client.Credentials, cfg uploaderID := "no" var uploadPath string - if cfg.AuthMode == VenafiCloudKeypair { + if cfg.TLSPKMode == VenafiCloudKeypair { // We don't do this for the VenafiCloudVenafiConnection mode because // the upload_path field is ignored in that mode. log.Info("Loading upload_path from \"venafi-cloud\" configuration.") @@ -904,7 +925,7 @@ func (c *Config) Dump() (string, error) { d, err := yaml.Marshal(&c) if err != nil { - return "", errors.Wrap(err, "failed to generate YAML dump of config") + return "", fmt.Errorf("failed to generate YAML dump of config: %w", err) } return string(d), nil diff --git a/pkg/agent/config_test.go b/pkg/agent/config_test.go index 414c1590..c07310b8 100644 --- a/pkg/agent/config_test.go +++ b/pkg/agent/config_test.go @@ -96,7 +96,7 @@ func Test_ValidateAndCombineConfig(t *testing.T) { withCmdLineFlags("--period", "99m", "--credentials-file", fakeCredsPath)) require.NoError(t, err) assert.Equal(t, testutil.Undent(` - INFO Authentication mode mode="Jetstack Secure OAuth" reason="--credentials-file was specified without --venafi-cloud" + INFO Configured to push to Venafi mode="Jetstack Secure OAuth" reason="--credentials-file was specified without --venafi-cloud" INFO Both the 'period' field and --period are set. Using the value provided with --period. `), gotLogs.String()) assert.Equal(t, 99*time.Minute, got.Period) @@ -178,7 +178,7 @@ func Test_ValidateAndCombineConfig(t *testing.T) { // The log line printed by pflag is not captured by the log recorder. assert.Equal(t, testutil.Undent(` - INFO Authentication mode mode="Jetstack Secure OAuth" reason="--credentials-file was specified without --venafi-cloud" + INFO Configured to push to Venafi mode="Jetstack Secure OAuth" reason="--credentials-file was specified without --venafi-cloud" INFO Using period from config period="1h0m0s" `), b.String()) }) @@ -194,12 +194,13 @@ func Test_ValidateAndCombineConfig(t *testing.T) { withoutCmdLineFlags(), ) assert.EqualError(t, err, testutil.Undent(` - no auth mode specified. You can use one of four auth modes: + no TLSPK mode specified and MachineHub mode is disabled. You must either enable the MachineHub mode (using --machine-hub), or enable one of the TLSPK modes. + To enable one of the TLSPK modes, you can: - Use (--venafi-cloud with --credentials-file) or (--client-id with --private-key-path) to use the Venafi Cloud Key Pair Service Account mode. - Use --venafi-connection for the Venafi Cloud VenafiConnection mode. - Use --credentials-file alone if you want to use the Jetstack Secure OAuth mode. - Use --api-token if you want to use the Jetstack Secure API Token mode. - `)) + Note that it is possible to use one of the TLSPK modes along with the MachineHub mode (--machine-hub).`)) assert.Nil(t, cl) }) @@ -227,7 +228,7 @@ func Test_ValidateAndCombineConfig(t *testing.T) { withCmdLineFlags("--credentials-file", credsPath), ) expect := CombinedConfig{ - AuthMode: "Jetstack Secure OAuth", + TLSPKMode: "Jetstack Secure OAuth", ClusterID: "example-cluster", DataGatherers: []DataGatherer{{Kind: "dummy", Name: "d1", @@ -276,7 +277,7 @@ func Test_ValidateAndCombineConfig(t *testing.T) { InputPath: "/home", OutputPath: "/nothome", UploadPath: "/testing/path", - AuthMode: VenafiCloudKeypair, + TLSPKMode: VenafiCloudKeypair, ClusterID: "the cluster name", BackoffMaxTime: 99 * time.Minute, InstallNS: "venafi", @@ -300,7 +301,7 @@ func Test_ValidateAndCombineConfig(t *testing.T) { withCmdLineFlags("--client-id", "5bc7d07c-45da-11ef-a878-523f1e1d7de1", "--private-key-path", privKeyPath), ) require.NoError(t, err) - assert.Equal(t, VenafiCloudKeypair, got.AuthMode) + assert.Equal(t, VenafiCloudKeypair, got.TLSPKMode) assert.IsType(t, &client.VenafiCloudClient{}, cl) }) @@ -389,7 +390,7 @@ func Test_ValidateAndCombineConfig(t *testing.T) { `)), withCmdLineFlags("--credentials-file", path)) require.NoError(t, err) - assert.Equal(t, CombinedConfig{Server: "https://api.venafi.eu", Period: time.Hour, OrganizationID: "foo", ClusterID: "bar", AuthMode: JetstackSecureOAuth, BackoffMaxTime: 10 * time.Minute, InstallNS: "venafi"}, got) + assert.Equal(t, CombinedConfig{Server: "https://api.venafi.eu", Period: time.Hour, OrganizationID: "foo", ClusterID: "bar", TLSPKMode: JetstackSecureOAuth, BackoffMaxTime: 10 * time.Minute, InstallNS: "venafi"}, got) assert.IsType(t, &client.OAuthClient{}, cl) }) @@ -468,7 +469,7 @@ func Test_ValidateAndCombineConfig(t *testing.T) { `)), withCmdLineFlags("--client-id", "5bc7d07c-45da-11ef-a878-523f1e1d7de1", "--private-key-path", path)) require.NoError(t, err) - assert.Equal(t, CombinedConfig{Server: "https://api.venafi.eu", Period: time.Hour, AuthMode: VenafiCloudKeypair, ClusterID: "the cluster name", UploadPath: "/foo/bar", BackoffMaxTime: 10 * time.Minute, InstallNS: "venafi"}, got) + assert.Equal(t, CombinedConfig{Server: "https://api.venafi.eu", Period: time.Hour, TLSPKMode: VenafiCloudKeypair, ClusterID: "the cluster name", UploadPath: "/foo/bar", BackoffMaxTime: 10 * time.Minute, InstallNS: "venafi"}, got) assert.IsType(t, &client.VenafiCloudClient{}, cl) }) @@ -490,7 +491,7 @@ func Test_ValidateAndCombineConfig(t *testing.T) { `)), withCmdLineFlags("--venafi-cloud", "--credentials-file", credsPath)) require.NoError(t, err) - assert.Equal(t, CombinedConfig{Server: "https://api.venafi.eu", Period: time.Hour, AuthMode: VenafiCloudKeypair, ClusterID: "the cluster name", UploadPath: "/foo/bar", BackoffMaxTime: 10 * time.Minute, InstallNS: "venafi"}, got) + assert.Equal(t, CombinedConfig{Server: "https://api.venafi.eu", Period: time.Hour, TLSPKMode: VenafiCloudKeypair, ClusterID: "the cluster name", UploadPath: "/foo/bar", BackoffMaxTime: 10 * time.Minute, InstallNS: "venafi"}, got) }) t.Run("venafi-cloud-keypair-auth: venafi-cloud.upload_path field is required", func(t *testing.T) { @@ -567,7 +568,7 @@ func Test_ValidateAndCombineConfig(t *testing.T) { assert.Equal(t, CombinedConfig{ Period: 1 * time.Hour, ClusterID: "the cluster name", - AuthMode: VenafiCloudVenafiConnection, + TLSPKMode: VenafiCloudVenafiConnection, VenConnName: "venafi-components", VenConnNS: "venafi", InstallNS: "venafi", @@ -593,13 +594,13 @@ func Test_ValidateAndCombineConfig(t *testing.T) { ) require.NoError(t, err) assert.Equal(t, testutil.Undent(` - INFO Authentication mode venConnName="venafi-components" mode="Venafi Cloud VenafiConnection" reason="--venafi-connection was specified" + INFO Configured to push to Venafi venConnName="venafi-components" mode="Venafi Cloud VenafiConnection" reason="--venafi-connection was specified" INFO ignoring the server field specified in the config file. In Venafi Cloud VenafiConnection mode, this field is not needed. INFO ignoring the venafi-cloud.upload_path field in the config file. In Venafi Cloud VenafiConnection mode, this field is not needed. INFO ignoring the venafi-cloud.uploader_id field in the config file. This field is not needed in Venafi Cloud VenafiConnection mode. INFO Using period from config period="1h0m0s" `), gotLogs.String()) - assert.Equal(t, VenafiCloudVenafiConnection, got.AuthMode) + assert.Equal(t, VenafiCloudVenafiConnection, got.TLSPKMode) assert.IsType(t, &client.VenConnClient{}, gotCl) }) @@ -614,7 +615,43 @@ func Test_ValidateAndCombineConfig(t *testing.T) { `)), withCmdLineFlags("--venafi-connection", "venafi-components")) require.NoError(t, err) - assert.Equal(t, VenafiCloudVenafiConnection, got.AuthMode) + assert.Equal(t, VenafiCloudVenafiConnection, got.TLSPKMode) + }) + + t.Run("machinehub only: username and password", func(t *testing.T) { + t.Setenv("POD_NAMESPACE", "venafi") + t.Setenv("KUBECONFIG", withFile(t, fakeKubeconfig)) + got, _, err := ValidateAndCombineConfig(discardLogs(), + withConfig(testutil.Undent(` + machineHub: + subdomain: foo + credentialsSecretName: secret-1 + period: 1h + `)), + withCmdLineFlags("--machine-hub")) + require.NoError(t, err) + assert.Equal(t, Off, got.TLSPKMode) + assert.Equal(t, true, got.MachineHubMode) + }) + + t.Run("machinehub + venafi-cloud-keypair-auth should work simultanously", func(t *testing.T) { + t.Setenv("POD_NAMESPACE", "venafi") + t.Setenv("KUBECONFIG", withFile(t, fakeKubeconfig)) + privKeyPath := withFile(t, fakePrivKeyPEM) + got, _, err := ValidateAndCombineConfig(discardLogs(), + withConfig(testutil.Undent(` + machineHub: + subdomain: foo + credentialsSecretName: secret-1 + period: 1h + venafi-cloud: + upload_path: /v1/tlspk/upload/clusterdata + cluster_id: foo + `)), + withCmdLineFlags("--machine-hub", "--venafi-cloud", "--client-id", "5bc7d07c-45da-11ef-a878-523f1e1d7de1", "--private-key-path", privKeyPath)) + require.NoError(t, err) + assert.Equal(t, VenafiCloudKeypair, got.TLSPKMode) + assert.Equal(t, true, got.MachineHubMode) }) } @@ -653,7 +690,7 @@ func Test_ValidateAndCombineConfig_VenafiCloudKeyPair(t *testing.T) { ) require.NoError(t, err) testutil.TrustCA(t, cl, cert) - assert.Equal(t, VenafiCloudKeypair, got.AuthMode) + assert.Equal(t, VenafiCloudKeypair, got.TLSPKMode) err = cl.PostDataReadingsWithOptions(ctx, nil, client.Options{ClusterName: "test cluster name"}) require.NoError(t, err) diff --git a/pkg/agent/run.go b/pkg/agent/run.go index 566f0e9b..a77a6135 100644 --- a/pkg/agent/run.go +++ b/pkg/agent/run.go @@ -327,7 +327,7 @@ func gatherAndOutputData(ctx context.Context, eventf Eventf, config CombinedConf } log.Info("Data saved to local file", "outputPath", config.OutputPath) } else { - group, gctx := errgroup.WithContext(ctx) + group, ctx := errgroup.WithContext(ctx) backOffCfg := backoff.NewExponentialBackOff() backOffCfg.InitialInterval = 30 * time.Second @@ -339,21 +339,21 @@ func gatherAndOutputData(ctx context.Context, eventf Eventf, config CombinedConf log.Info("Warning: PushingErr: retrying", "in", t, "reason", err) } - if cfg.MachineHubMode { + if config.MachineHubMode { post := func() error { log.Info("machine hub mode not yet implemented") return nil } - group.Go(backoff.RetryNotify(post, backOffCfg, notifyFn)) + group.Go(func() error { return backoff.RetryNotify(post, backOffCfg, notifyFn) }) } - if cfg.AuthMode != NoTLSPK { + if config.TLSPKMode != Off { post := func() error { return postData(klog.NewContext(ctx, log), config, preflightClient, readings) } - group.Go(backoff.RetryNotify(post, backOffCfg, notifyFn)) + group.Go(func() error { return backoff.RetryNotify(post, backOffCfg, notifyFn) }) } groupErr := group.Wait() @@ -420,7 +420,7 @@ func postData(ctx context.Context, config CombinedConfig, preflightClient client log.V(logs.Debug).Info("Posting data", "baseURL", baseURL) - if config.AuthMode == VenafiCloudKeypair || config.AuthMode == VenafiCloudVenafiConnection { + if config.TLSPKMode == VenafiCloudKeypair || config.TLSPKMode == VenafiCloudVenafiConnection { // orgID and clusterID are not required for Venafi Cloud auth err := preflightClient.PostDataReadingsWithOptions(ctx, readings, client.Options{ ClusterName: config.ClusterID,