Skip to content

Commit c6bbefb

Browse files
committed
machinehub: add the --machine-hub flag and add tests
1 parent 7c45a44 commit c6bbefb

File tree

3 files changed

+136
-78
lines changed

3 files changed

+136
-78
lines changed

pkg/agent/config.go

Lines changed: 78 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package agent
22

33
import (
4+
"errors"
45
"fmt"
56
"io"
67
"net/url"
@@ -10,7 +11,6 @@ import (
1011

1112
"github.com/go-logr/logr"
1213
"github.com/hashicorp/go-multierror"
13-
"github.com/pkg/errors"
1414
"github.com/spf13/cobra"
1515
"gopkg.in/yaml.v3"
1616
"k8s.io/client-go/rest"
@@ -63,7 +63,7 @@ type Config struct {
6363
// Skips label keys that match the given set of regular expressions.
6464
ExcludeLabelKeysRegex []string `yaml:"exclude-label-keys-regex"`
6565

66-
// MachineHub holds config specific to MachineHub mode
66+
// MachineHub holds config specific to MachineHub mode.
6767
MachineHub MachineHubConfig `yaml:"machineHub"`
6868
}
6969

@@ -94,13 +94,14 @@ type VenafiCloudConfig struct {
9494

9595
// MachineHubConfig holds configuration values specific to the CyberArk Machine Hub integration
9696
type MachineHubConfig struct {
97-
// Subdomain is the subdomain indicating where data should be pushed. Used for
98-
// querying the Service Discovery Service to discover the Identity API URL
97+
// Subdomain is the subdomain indicating where data should be pushed. Used
98+
// for querying the Service Discovery Service to discover the Identity API
99+
// URL.
99100
Subdomain string `yaml:"subdomain"`
100101

101-
// CredentialsSecretName is the name of a Kubernetes Secret in the same namespace as
102-
// the agent, which will be watched for a username and password to send to CyberArk
103-
// Identity for authentication.
102+
// CredentialsSecretName is the name of a Kubernetes Secret in the same
103+
// namespace as the agent, which will be watched for a username and password
104+
// to send to CyberArk Identity for authentication.
104105
CredentialsSecretName string `yaml:"credentialsSecretName"`
105106
}
106107

@@ -132,7 +133,7 @@ type AgentCmdFlags struct {
132133
// --credentials-file.
133134
VenafiCloudMode bool
134135

135-
// MachineHubMode configures the agent to send data to CyberArk Machine Hub
136+
// MachineHubMode configures the agent to send data to CyberArk Machine Hub.
136137
MachineHubMode bool
137138

138139
// ClientID (--client-id) is the clientID in case of Venafi Cloud Key Pair
@@ -335,34 +336,44 @@ func InitAgentCmdFlags(c *cobra.Command, cfg *AgentCmdFlags) {
335336
)
336337
c.PersistentFlags().MarkDeprecated("disable-compression", "no longer has an effect")
337338

339+
c.PersistentFlags().BoolVar(
340+
&cfg.MachineHubMode,
341+
"machine-hub",
342+
false,
343+
"Enables MachineHub mode. The agent will push data to CyberArk MachineHub. Can be used in conjunction with --venafi-cloud.",
344+
)
345+
338346
}
339347

340-
// AuthMode controls how to authenticate to TLSPK / Jetstack Secure. Only one AuthMode
341-
// may be provided if using using those backends.
342-
type AuthMode string
348+
// TLSPKMode controls how to authenticate to TLSPK / Jetstack Secure. Only one
349+
// TLSPKMode may be provided if using using those backends.
350+
type TLSPKMode string
343351

344352
const (
345-
JetstackSecureOAuth AuthMode = "Jetstack Secure OAuth"
346-
JetstackSecureAPIToken AuthMode = "Jetstack Secure API Token"
347-
VenafiCloudKeypair AuthMode = "Venafi Cloud Key Pair Service Account"
348-
VenafiCloudVenafiConnection AuthMode = "Venafi Cloud VenafiConnection"
349-
350-
NoTLSPK AuthMode = "MachineHub only"
353+
JetstackSecureOAuth TLSPKMode = "Jetstack Secure OAuth"
354+
JetstackSecureAPIToken TLSPKMode = "Jetstack Secure API Token"
355+
VenafiCloudKeypair TLSPKMode = "Venafi Cloud Key Pair Service Account"
356+
VenafiCloudVenafiConnection TLSPKMode = "Venafi Cloud VenafiConnection"
357+
358+
// It is possible to push to both MachineHub and TLSPK. With this mode, the
359+
// agent will only push to MachineHub and not to TLSPK.
360+
Off TLSPKMode = "MachineHub only"
351361
)
352362

353363
// The command-line flags and the config file are combined into this struct by
354364
// ValidateAndCombineConfig.
355365
type CombinedConfig struct {
356-
AuthMode AuthMode
357-
358-
// Used by all modes.
359-
ClusterID string
360366
DataGatherers []DataGatherer
361367
Period time.Duration
362368
BackoffMaxTime time.Duration
369+
InstallNS string
363370
StrictMode bool
364371
OneShot bool
365-
InstallNS string
372+
373+
TLSPKMode TLSPKMode
374+
375+
// Used by all TLSPK modes.
376+
ClusterID string
366377

367378
// Used by JetstackSecureOAuth, JetstackSecureAPIToken, and
368379
// VenafiCloudKeypair. Ignored in VenafiCloudVenafiConnection mode.
@@ -388,10 +399,10 @@ type CombinedConfig struct {
388399
OutputPath string
389400
InputPath string
390401

391-
// MachineHub-related settings
392-
MachineHubMode bool
393-
394-
MachineHub MachineHubConfig
402+
// MachineHub-related settings.
403+
MachineHubMode bool
404+
MachineHubSubdomain string
405+
MachineHubCredentialsSecretName string
395406
}
396407

397408
// ValidateAndCombineConfig combines and validates the input configuration with
@@ -414,12 +425,16 @@ func ValidateAndCombineConfig(log logr.Logger, cfg Config, flags AgentCmdFlags)
414425
}
415426

416427
res.MachineHubMode = true
417-
res.MachineHubConfig = cfg.MachineHubConfig
428+
res.MachineHubSubdomain = cfg.MachineHub.Subdomain
429+
res.MachineHubCredentialsSecretName = cfg.MachineHub.CredentialsSecretName
430+
431+
keysAndValues := []any{"credentialsSecretName", res.MachineHubCredentialsSecretName}
432+
log.V(logs.Info).Info("Will push to CyberArk MachineHub using a username and password loaded from a Kubernetes Secret", keysAndValues...)
418433
}
419434

420435
{
421436
var (
422-
mode AuthMode
437+
mode TLSPKMode
423438
reason string
424439
keysAndValues []any
425440
)
@@ -448,24 +463,28 @@ func ValidateAndCombineConfig(log logr.Logger, cfg Config, flags AgentCmdFlags)
448463
reason = "--credentials-file was specified without --venafi-cloud"
449464
default:
450465
if !flags.MachineHubMode {
451-
return CombinedConfig{}, nil, fmt.Errorf("no auth mode specified and MachineHub mode is disabled. You can use one of four auth modes:\n" +
466+
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" +
467+
"To enable one of the TLSPK modes, you can:\n" +
452468
" - Use (--venafi-cloud with --credentials-file) or (--client-id with --private-key-path) to use the " + string(VenafiCloudKeypair) + " mode.\n" +
453469
" - Use --venafi-connection for the " + string(VenafiCloudVenafiConnection) + " mode.\n" +
454470
" - Use --credentials-file alone if you want to use the " + string(JetstackSecureOAuth) + " mode.\n" +
455-
" - Use --api-token if you want to use the " + string(JetstackSecureAPIToken) + " mode.\n")
471+
" - Use --api-token if you want to use the " + string(JetstackSecureAPIToken) + " mode.\n" +
472+
"Note that it is possible to use one of the TLSPK modes along with the MachineHub mode (--machine-hub).")
456473
}
457474

458-
mode = NoTLSPK
459-
reason = "only MachineHub has been configured as a backend"
475+
mode = Off
460476
}
461477

462-
res.AuthMode = mode
463478
keysAndValues = append(keysAndValues, "mode", mode, "reason", reason)
464-
log.V(logs.Debug).Info("Authentication mode", keysAndValues...)
479+
if mode != Off {
480+
log.V(logs.Debug).Info("Configured to push to Venafi", keysAndValues...)
481+
}
482+
483+
res.TLSPKMode = mode
465484
}
466485

467486
// Validation and defaulting of `server` and the deprecated `endpoint.path`.
468-
if res.AuthMode != NoTLSPK {
487+
if res.TLSPKMode != Off {
469488
// Only relevant if using TLSPK backends
470489
hasEndpointField := cfg.Endpoint.Host != "" && cfg.Endpoint.Path != ""
471490
hasServerField := cfg.Server != ""
@@ -488,7 +507,7 @@ func ValidateAndCombineConfig(log logr.Logger, cfg Config, flags AgentCmdFlags)
488507
endpointPath = cfg.Endpoint.Path
489508
case !hasServerField && !hasEndpointField:
490509
server = "https://preflight.jetstack.io"
491-
if res.AuthMode == VenafiCloudKeypair {
510+
if res.TLSPKMode == VenafiCloudKeypair {
492511
// The VenafiCloudVenafiConnection mode doesn't need a server.
493512
server = client.VenafiCloudProdURL
494513
}
@@ -497,7 +516,7 @@ func ValidateAndCombineConfig(log logr.Logger, cfg Config, flags AgentCmdFlags)
497516
if urlErr != nil || url.Hostname() == "" {
498517
errs = multierror.Append(errs, fmt.Errorf("server %q is not a valid URL", server))
499518
}
500-
if res.AuthMode == VenafiCloudVenafiConnection && server != "" {
519+
if res.TLSPKMode == VenafiCloudVenafiConnection && server != "" {
501520
log.Info(fmt.Sprintf("ignoring the server field specified in the config file. In %s mode, this field is not needed.", VenafiCloudVenafiConnection))
502521
server = ""
503522
}
@@ -509,9 +528,9 @@ func ValidateAndCombineConfig(log logr.Logger, cfg Config, flags AgentCmdFlags)
509528
{
510529
var uploadPath string
511530
switch {
512-
case res.AuthMode == VenafiCloudKeypair:
531+
case res.TLSPKMode == VenafiCloudKeypair:
513532
if cfg.VenafiCloud == nil || cfg.VenafiCloud.UploadPath == "" {
514-
errs = multierror.Append(errs, fmt.Errorf("the venafi-cloud.upload_path field is required when using the %s mode", res.AuthMode))
533+
errs = multierror.Append(errs, fmt.Errorf("the venafi-cloud.upload_path field is required when using the %s mode", res.TLSPKMode))
515534
break // Skip to the end of the switch statement.
516535
}
517536
_, urlErr := url.Parse(cfg.VenafiCloud.UploadPath)
@@ -521,14 +540,14 @@ func ValidateAndCombineConfig(log logr.Logger, cfg Config, flags AgentCmdFlags)
521540
}
522541

523542
uploadPath = cfg.VenafiCloud.UploadPath
524-
case res.AuthMode == VenafiCloudVenafiConnection:
543+
case res.TLSPKMode == VenafiCloudVenafiConnection:
525544
// The venafi-cloud.upload_path was initially meant to let users
526545
// configure HTTP proxies, but it has never been used since HTTP
527546
// proxies don't rewrite paths. Thus, we've disabled the ability to
528547
// change this value with the new --venafi-connection flag, and this
529548
// field is simply ignored.
530549
if cfg.VenafiCloud != nil && cfg.VenafiCloud.UploadPath != "" {
531-
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))
550+
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))
532551
}
533552
uploadPath = ""
534553
}
@@ -546,26 +565,26 @@ func ValidateAndCombineConfig(log logr.Logger, cfg Config, flags AgentCmdFlags)
546565
// https://venafi.atlassian.net/browse/VC-35385 is done.
547566
{
548567
if cfg.VenafiCloud != nil && cfg.VenafiCloud.UploaderID != "" {
549-
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))
568+
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))
550569
}
551570
}
552571

553572
// Validation of `cluster_id` and `organization_id`.
554-
if res.AuthMode != NoTLSPK {
573+
if res.TLSPKMode != Off {
555574
var clusterID string
556575
var organizationID string // Only used by the old jetstack-secure mode.
557576
switch {
558-
case res.AuthMode == VenafiCloudKeypair:
577+
case res.TLSPKMode == VenafiCloudKeypair:
559578
if cfg.ClusterID == "" {
560-
errs = multierror.Append(errs, fmt.Errorf("cluster_id is required in %s mode", res.AuthMode))
579+
errs = multierror.Append(errs, fmt.Errorf("cluster_id is required in %s mode", res.TLSPKMode))
561580
}
562581
clusterID = cfg.ClusterID
563-
case res.AuthMode == VenafiCloudVenafiConnection:
582+
case res.TLSPKMode == VenafiCloudVenafiConnection:
564583
if cfg.ClusterID == "" {
565-
errs = multierror.Append(errs, fmt.Errorf("cluster_id is required in %s mode", res.AuthMode))
584+
errs = multierror.Append(errs, fmt.Errorf("cluster_id is required in %s mode", res.TLSPKMode))
566585
}
567586
clusterID = cfg.ClusterID
568-
case res.AuthMode == JetstackSecureOAuth || res.AuthMode == JetstackSecureAPIToken:
587+
case res.TLSPKMode == JetstackSecureOAuth || res.TLSPKMode == JetstackSecureAPIToken:
569588
if cfg.OrganizationID == "" {
570589
errs = multierror.Append(errs, fmt.Errorf("organization_id is required"))
571590
}
@@ -625,7 +644,7 @@ func ValidateAndCombineConfig(log logr.Logger, cfg Config, flags AgentCmdFlags)
625644
res.InstallNS = installNS
626645

627646
// Validation of --venafi-connection and --venafi-connection-namespace.
628-
if res.AuthMode == VenafiCloudVenafiConnection {
647+
if res.TLSPKMode == VenafiCloudVenafiConnection {
629648
res.VenConnName = flags.VenConnName
630649
var venConnNS string = flags.VenConnNS
631650
if flags.VenConnNS == "" {
@@ -692,7 +711,7 @@ func validateCredsAndCreateClient(log logr.Logger, flagCredentialsPath, flagClie
692711
var preflightClient client.Client
693712
metadata := &api.AgentMetadata{Version: version.PreflightVersion, ClusterID: cfg.ClusterID}
694713
switch {
695-
case cfg.AuthMode == JetstackSecureOAuth:
714+
case cfg.TLSPKMode == JetstackSecureOAuth:
696715
// Note that there are no command line flags to configure the
697716
// JetstackSecureOAuth mode.
698717
credsBytes, err := readCredentialsFile(flagCredentialsPath)
@@ -711,7 +730,7 @@ func validateCredsAndCreateClient(log logr.Logger, flagCredentialsPath, flagClie
711730
if err != nil {
712731
errs = multierror.Append(errs, err)
713732
}
714-
case cfg.AuthMode == VenafiCloudKeypair:
733+
case cfg.TLSPKMode == VenafiCloudKeypair:
715734
var creds client.Credentials
716735

717736
if flagClientID != "" && flagCredentialsPath != "" {
@@ -754,7 +773,7 @@ func validateCredsAndCreateClient(log logr.Logger, flagCredentialsPath, flagClie
754773
if err != nil {
755774
errs = multierror.Append(errs, err)
756775
}
757-
case cfg.AuthMode == VenafiCloudVenafiConnection:
776+
case cfg.TLSPKMode == VenafiCloudVenafiConnection:
758777
var restCfg *rest.Config
759778
restCfg, err := kubeconfig.LoadRESTConfig("")
760779
if err != nil {
@@ -766,18 +785,20 @@ func validateCredsAndCreateClient(log logr.Logger, flagCredentialsPath, flagClie
766785
if err != nil {
767786
errs = multierror.Append(errs, err)
768787
}
769-
case cfg.AuthMode == JetstackSecureAPIToken:
788+
case cfg.TLSPKMode == JetstackSecureAPIToken:
770789
var err error
771790
preflightClient, err = client.NewAPITokenClient(metadata, flagAPIToken, cfg.Server)
772791
if err != nil {
773792
errs = multierror.Append(errs, err)
774793
}
794+
case cfg.TLSPKMode == Off:
795+
// No client needed in this mode.
775796
default:
776-
panic(fmt.Errorf("programmer mistake: auth mode not implemented: %s", cfg.AuthMode))
797+
panic(fmt.Errorf("programmer mistake: auth mode not implemented: %s", cfg.TLSPKMode))
777798
}
778799

779800
if errs != nil {
780-
return nil, fmt.Errorf("failed loading config using the %s mode: %w", cfg.AuthMode, errs)
801+
return nil, fmt.Errorf("failed loading config using the %s mode: %w", cfg.TLSPKMode, errs)
781802
}
782803

783804
return preflightClient, nil
@@ -814,7 +835,7 @@ func createCredentialClient(log logr.Logger, credentials client.Credentials, cfg
814835
uploaderID := "no"
815836

816837
var uploadPath string
817-
if cfg.AuthMode == VenafiCloudKeypair {
838+
if cfg.TLSPKMode == VenafiCloudKeypair {
818839
// We don't do this for the VenafiCloudVenafiConnection mode because
819840
// the upload_path field is ignored in that mode.
820841
log.Info("Loading upload_path from \"venafi-cloud\" configuration.")
@@ -904,7 +925,7 @@ func (c *Config) Dump() (string, error) {
904925
d, err := yaml.Marshal(&c)
905926

906927
if err != nil {
907-
return "", errors.Wrap(err, "failed to generate YAML dump of config")
928+
return "", fmt.Errorf("failed to generate YAML dump of config: %w", err)
908929
}
909930

910931
return string(d), nil

0 commit comments

Comments
 (0)